package com.cloudkinto.service.order.outbound.impl;

import cn.afterturn.easypoi.excel.ExcelExportUtil;
import cn.afterturn.easypoi.excel.entity.ExportParams;
import cn.afterturn.easypoi.excel.entity.TemplateExportParams;
import cn.afterturn.easypoi.excel.entity.enmus.ExcelType;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateField;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
import com.cloudkinto.bo.api.ImportUpdateTrackingNumberAutoSendApiBo;
import com.cloudkinto.bo.order.allocate.AllocatePlanQueryBo;
import com.cloudkinto.bo.order.inbound.InputBoxTypeQueryBo;
import com.cloudkinto.bo.order.outbound.ImportNormalOutboundExpressBo;
import com.cloudkinto.bo.order.outbound.NormalOrderOutboundQueryBo;
import com.cloudkinto.bo.order.outbound.PrintExpressSheetBo;
import com.cloudkinto.bo.order.outbound.TransferOrderOutboundQueryBo;
import com.cloudkinto.common.BeanConvert;
import com.cloudkinto.common.DynamicTablePolicy;
import com.cloudkinto.common.ExcelExportTitleStyle;
import com.cloudkinto.common.RabbitQueueConstant;
import com.cloudkinto.common.constant.SysConstant;
import com.cloudkinto.common.exception.BizException;
import com.cloudkinto.common.utils.ExcelUtils;
import com.cloudkinto.common.utils.StringsUtil;
import com.cloudkinto.common.utils.dict.StaticDict;
import com.cloudkinto.dao.AllocatePlanDao;
import com.cloudkinto.dao.OrderOutboundDao;
import com.cloudkinto.dto.order.outbound.*;
import com.cloudkinto.dto.print.BoxShippingListTableDto;
import com.cloudkinto.dto.print.SkuShippingListTableDto;
import com.cloudkinto.dto.print.TransferPickListByBoxTableDto;
import com.cloudkinto.dto.print.TransferPickListBySkuTableDto;
import com.cloudkinto.dto.print.common.DynamicTableDto;
import com.cloudkinto.entity.*;
import com.cloudkinto.enumType.address.AddressBookTypeEnum;
import com.cloudkinto.enumType.common.CountryEnum;
import com.cloudkinto.enumType.operate.OperateTypeEnum;
import com.cloudkinto.enumType.order.allocate.AllocatePlanStatus;
import com.cloudkinto.enumType.order.outbound.*;
import com.cloudkinto.service.address.AddressBookService;
import com.cloudkinto.service.channel.LcShippingStorageService;
import com.cloudkinto.service.channel.LogisticsChannelService;
import com.cloudkinto.service.company.CompanyService;
import com.cloudkinto.service.file.FileService;
import com.cloudkinto.service.material.MaterialService;
import com.cloudkinto.service.operate.OperateRecordService;
import com.cloudkinto.service.order.allocate.AllocatePlanItemService;
import com.cloudkinto.service.order.allocate.AllocatePlanService;
import com.cloudkinto.service.order.box.OrderBoxProductService;
import com.cloudkinto.service.order.box.OrderBoxService;
import com.cloudkinto.service.order.outbound.*;
import com.cloudkinto.service.order.outbound.vo.OrderOutboundImportTrackNumberReq;
import com.cloudkinto.service.order.picking.OutboundPickingService;
import com.cloudkinto.service.platform.PlatformProductMappingService;
import com.cloudkinto.service.platform.PlatformProductService;
import com.cloudkinto.service.platform.PlatformStoreService;
import com.cloudkinto.service.product.ProductCategoryService;
import com.cloudkinto.service.product.ProductService;
import com.cloudkinto.service.storage.StorageLocationService;
import com.cloudkinto.service.system.SystemSetService;
import com.cloudkinto.service.warehouse.WarehouseService;
import com.cloudkinto.utils.AsposePdfUtils;
import com.cloudkinto.utils.BarCodeUtils;
import com.cloudkinto.vo.common.CommonBatchOperationVo;
import com.cloudkinto.vo.common.CommonKvVo;
import com.cloudkinto.vo.order.allocate.AllocatePlanBatchVo;
import com.cloudkinto.vo.order.inbound.InputBoxTypeNumberVo;
import com.cloudkinto.vo.order.outbound.NormalOutboundVo;
import com.cloudkinto.vo.order.outbound.PackageSizeVo;
import com.cloudkinto.vo.order.outbound.TransferOutboundVo;
import com.cloudkinto.vo.system.OmsSystemEntity;
import com.cloudkinto.vo.system.omp.OmpSystemEntity;
import com.cloudkinto.vo.system.wms.WmsSystemEntity;
import com.deepoove.poi.XWPFTemplate;
import com.deepoove.poi.config.Configure;
import com.deepoove.poi.config.ConfigureBuilder;
import com.deepoove.poi.data.*;
import com.deepoove.poi.util.PoitlIOUtils;
import com.itextpdf.text.Document;
import com.itextpdf.text.Image;
import com.itextpdf.text.PageSize;
import com.itextpdf.text.pdf.PdfCopy;
import com.itextpdf.text.pdf.PdfImportedPage;
import com.itextpdf.text.pdf.PdfReader;
import com.itextpdf.text.pdf.PdfWriter;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.ss.usermodel.Workbook;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionTemplate;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.math.RoundingMode;
import java.net.URLEncoder;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

/**
 * @author long
 * @Description 出库单导入导出打印
 * @Date 2024/12/30 20:06
 */
@Service
@RequiredArgsConstructor
@Transactional(rollbackFor = Exception.class)
@Slf4j
public class OutboundImportExportServiceImpl implements OutboundImportExportService {

    private final OperateRecordService operateRecordService;
    private final OrderOutboundProductService outboundProductService;
    private final OutboundPickingService outboundPickingService;
    private final WarehouseService warehouseService;
    private final CompanyService companyService;
    private final LogisticsChannelService logisticsChannelService;
    private final LcShippingStorageService shippingStorageService;
    private final ProductCategoryService productCategoryService;
    private final OrderOutboundPackageService outboundPackageService;
    private final OrderOutboundPackageMaterialService outboundPackageMaterialService;
    private final ProductService productService;
    private final OrderBoxService boxService;
    private final StorageLocationService storageLocationService;
    private final MaterialService materialService;
    private final FileService fileService;
    private final SystemSetService systemSetService;
    private final OutboundService outboundService;
    private final OrderOutboundDao outboundDao;
    private final OrderBoxProductService boxProductService;
    private final AllocatePlanService allocatePlanService;
    private final AllocatePlanDao allocatePlanDao;
    private final AllocatePlanItemService allocatePlanItemService;
    private final AddressBookService addressBookService;
    private final RabbitTemplate rabbitTemplate;
    private final PlatformStoreService platformStoreService;
    private final PlatformProductService platformProductService;
    private final PlatformProductMappingService platformProductMappingService;
    private final TransactionTemplate transactionTemplate;
    private final ThreadPoolExecutor threadPoolExecutor;

    @Value("${gbc.customer}")
    private String customer;

    @Override
    public void printNormalShippingList(Long id, HttpServletResponse response) throws IOException {
        response.setHeader("content-Type", "application/pdf");
        response.setHeader("Access-Control-Expose-Headers", "Content-Disposition");
        response.setHeader("Content-Disposition", "attachment;filename=printNormalShippingList.pdf");
        OrderOutboundDo outboundDo = outboundService.getById(id);
        String outboundNo = outboundDo.getOutboundNo();
        byte[] outboundNoBarcode = BarCodeUtils.generateBarCode128(
                outboundNo,
                true,
                false,
                256,
                15,
                0.3f,
                0f,
                0f
        );
        ByteArrayInputStream outboundNoBarcodeImg = new ByteArrayInputStream(outboundNoBarcode);
        Long whId = outboundDo.getWhId();
        WarehouseDo warehouseDo = warehouseService.getById(whId);
        //通用参数
        Map<String, Object> data = new HashMap<>();
        data.put("outboundNo", outboundNo);
        data.put("outboundNoBarcode", Pictures.ofStream(outboundNoBarcodeImg, PictureType.PNG).size(240, 60).create());
        data.put("whName", warehouseDo.getWhNameCn());
        data.put("printTime", DateUtil.date());
        List<OrderOutboundProductDo> outboundProductDoList = outboundProductService.lambdaQuery()
                .eq(OrderOutboundProductDo::getOutboundId, id)
                .list();
        Map<Long, ProductDo> productIdMatch = productService.lambdaQuery()
                .in(ProductDo::getId, outboundProductDoList.stream().map(OrderOutboundProductDo::getProductId).collect(Collectors.toList()))
                .list().stream().collect(Collectors.toMap(ProductDo::getId, val -> val));
        AtomicInteger i = new AtomicInteger(1);
        List<SkuShippingListTableDto> skuShippingListTableDtoList = new ArrayList<>();
        for (OrderOutboundProductDo outboundProductDo : outboundProductDoList) {
            ProductDo productDo = productIdMatch.get(outboundProductDo.getProductId());
            if (productDo == null) {
                continue;
            }
            SkuShippingListTableDto dto = new SkuShippingListTableDto();
            dto.setNo(String.valueOf(i.getAndIncrement()));
            dto.setJancode(productDo.getJanCode());
            dto.setSku(productDo.getSku());
            dto.setProductName(productDo.getProductName());
            dto.setQty(String.valueOf(outboundProductDo.getForecastQty()));
            skuShippingListTableDtoList.add(dto);
        }
        data.put(
                "table",
                this.getSkuShippingListTableData(
                        skuShippingListTableDtoList,
                        new DynamicTableDto(0, 0, 0, 5, 1)
                )
        );
        data.put("skuKind", outboundProductDoList.size());
        data.put("skuTotalQty", outboundProductDoList.stream().map(OrderOutboundProductDo::getForecastQty).reduce(0, Integer::sum));
        ConfigureBuilder configureBuilder = Configure.builder();
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ServletOutputStream out = response.getOutputStream();
        //模板配置
        configureBuilder.bind("table", new DynamicTablePolicy());
        XWPFTemplate template = XWPFTemplate.compile(this.getClass().getClassLoader().getResourceAsStream("wordTemplates/shippingList-normal.docx"), configureBuilder.build()).render(data);
        template.writeAndClose(bos);
        //转换为pdf
        ByteArrayOutputStream pdf = AsposePdfUtils.word2pdf(new ByteArrayInputStream(bos.toByteArray()));
        out.write(pdf.toByteArray());
        bos.flush();
        out.flush();
        PoitlIOUtils.closeQuietlyMulti(template, bos, out, pdf);
    }

    @Override
    public void printTransferShippingList(Long id, HttpServletResponse response) throws IOException {
        response.setHeader("Content-Type", "application/pdf");
        response.setHeader("Access-Control-Expose-Headers", "Content-Disposition");
        response.setHeader("Content-Disposition", "attachment;filename=printTransferShippingList.pdf");
        OrderOutboundDo outboundDo = outboundService.getById(id);
        String outboundNo = outboundDo.getOutboundNo();
        byte[] outboundNoBarcode = BarCodeUtils.generateBarCode128(
                outboundNo,
                true,
                false,
                256,
                15,
                0.3f,
                0f,
                0f
        );
        ByteArrayInputStream outboundNoBarcodeImg = new ByteArrayInputStream(outboundNoBarcode);
        //获取客户信息
        CompanyDo companyDo = companyService.getById(outboundDo.getCompanyId());
        String company = companyDo.getName() + "(" + companyDo.getCompanyCode() + ")";
        LogisticsChannelDo logisticsChannelDo = logisticsChannelService.getById(outboundDo.getLogisticsChannelId());
        //通用参数
        Map<String, Object> data = new HashMap<>();
        data.put("outboundNo", outboundNo);
        data.put("company", company);
        data.put("printTime", DateUtil.date());
        data.put("destinationType", DestinationTypeEnum.getDescByVal(outboundDo.getDestinationType()));
        data.put("FBAShipmentID", outboundDo.getFbaShipmentId() == null ? "-" : outboundDo.getFbaShipmentId());
        data.put("FBATrackingNo", outboundDo.getShipmentTrackingCode() == null ? "-" : outboundDo.getShipmentTrackingCode());
        data.put("refOrderNo", outboundDo.getReferenceNumber() == null ? "-" : outboundDo.getReferenceNumber());
        data.put("logisticsChannelCode", logisticsChannelDo.getChannelCode());
        data.put("trackingNumber", outboundDo.getTrackingNumber() == null ? "-" : outboundDo.getTrackingNumber());
        data.put("expectedArrivalDate", outboundDo.getDeliveryDate() == null ? "-" : outboundDo.getDeliveryDate());
        data.put("address1", StrUtil.isEmpty(outboundDo.getAddress1()) ? "" : outboundDo.getAddress1());
        data.put("address2", StrUtil.isEmpty(outboundDo.getAddress2()) ? "" : outboundDo.getAddress2());
        data.put("houseNum", StrUtil.isEmpty(outboundDo.getHouseNum()) ? "" : outboundDo.getHouseNum());
        List<OrderOutboundProductDo> outboundProductDoList = outboundProductService.lambdaQuery()
                .eq(OrderOutboundProductDo::getOutboundId, id)
                .list();
        ConfigureBuilder configureBuilder = Configure.builder();
        XWPFTemplate template;
        ByteArrayOutputStream pdf;
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ServletOutputStream out = response.getOutputStream();
        //出库方式
        Integer outboundMode = outboundDo.getOutboundMode();
        switch (outboundMode) {
            case 1:
                //按产品
                data.put("outboundNoBarcode", Pictures.ofStream(outboundNoBarcodeImg, PictureType.PNG).size(235, 60).create());
                Map<Long, ProductDo> productIdMatch = productService.lambdaQuery()
                        .in(ProductDo::getId, outboundProductDoList.stream().map(OrderOutboundProductDo::getProductId).collect(Collectors.toList()))
                        .list().stream().collect(Collectors.toMap(ProductDo::getId, val -> val));
                AtomicInteger i = new AtomicInteger(1);
                List<SkuShippingListTableDto> skuShippingListTableDtoList = new ArrayList<>();
                for (OrderOutboundProductDo outboundProductDo : outboundProductDoList) {
                    ProductDo productDo = productIdMatch.get(outboundProductDo.getProductId());
                    if (productDo == null) {
                        continue;
                    }
                    SkuShippingListTableDto dto = new SkuShippingListTableDto();
                    dto.setNo(String.valueOf(i.getAndIncrement()));
                    dto.setJancode(productDo.getJanCode());
                    dto.setSku(productDo.getSku());
                    dto.setProductName(productDo.getProductName());
                    dto.setQty(String.valueOf(outboundProductDo.getForecastQty()));
                    skuShippingListTableDtoList.add(dto);
                }
                data.put(
                        "table",
                        this.getSkuShippingListTableData(
                                skuShippingListTableDtoList,
                                new DynamicTableDto(0, 0, 0, 5, 1)
                        )
                );
                data.put("skuKind", outboundProductDoList.size());
                data.put("skuTotalQty", outboundProductDoList.stream().map(OrderOutboundProductDo::getForecastQty).reduce(0, Integer::sum));
                //模板配置
                configureBuilder.bind("table", new DynamicTablePolicy());
                template = XWPFTemplate.compile(this.getClass().getClassLoader().getResourceAsStream("wordTemplates/transferShippingListBySku.docx"), configureBuilder.build()).render(data);
                template.writeAndClose(bos);
                //转换为pdf
                pdf = AsposePdfUtils.word2pdf(new ByteArrayInputStream(bos.toByteArray()));
                out.write(pdf.toByteArray());
                bos.flush();
                out.flush();
                PoitlIOUtils.closeQuietlyMulti(template, bos, out, pdf);
                break;
            case 2:
                //按箱
                data.put("outboundNoBarcode", Pictures.ofStream(outboundNoBarcodeImg, PictureType.PNG).size(235, 60).create());
                Map<String, OrderBoxDo> boxMatch = boxService.lambdaQuery()
                        .in(OrderBoxDo::getBoxTypeNumber, outboundProductDoList.stream().map(OrderOutboundProductDo::getBoxTypeNumber).collect(Collectors.toList()))
                        .groupBy(OrderBoxDo::getBoxTypeNumber)
                        .list().stream().collect(Collectors.toMap(OrderBoxDo::getBoxTypeNumber, val -> val, (o1, o2) -> o1));
                AtomicInteger j = new AtomicInteger(1);
                List<BoxShippingListTableDto> boxShippingListTableDtoList = new ArrayList<>();
                for (OrderOutboundProductDo outboundProductDo : outboundProductDoList) {
                    BoxShippingListTableDto dto = new BoxShippingListTableDto();
                    dto.setNo(String.valueOf(j.getAndIncrement()));
                    dto.setBoxTypeNumber(outboundProductDo.getBoxTypeNumber());
                    dto.setCustomizeBarcode(boxMatch.get(outboundProductDo.getBoxTypeNumber()).getCustomizeBarcode());
                    dto.setQty(String.valueOf(outboundProductDo.getForecastBoxQty()));
                    boxShippingListTableDtoList.add(dto);
                }
                data.put(
                        "table",
                        this.getBoxShippingListTableData(
                                boxShippingListTableDtoList,
                                new DynamicTableDto(0, 0, 0, 4, 1)
                        )
                );
                data.put("boxKind", outboundProductDoList.size());
                data.put("boxTotalQty", outboundProductDoList.stream().map(OrderOutboundProductDo::getForecastBoxQty).reduce(0, Integer::sum));
                //模板配置
                configureBuilder.bind("table", new DynamicTablePolicy());
                template = XWPFTemplate.compile(this.getClass().getClassLoader().getResourceAsStream("wordTemplates/transferShippingListByBox.docx"), configureBuilder.build()).render(data);
                template.writeAndClose(bos);
                //转换为pdf
                pdf = AsposePdfUtils.word2pdf(new ByteArrayInputStream(bos.toByteArray()));
                out.write(pdf.toByteArray());
                bos.flush();
                out.flush();
                PoitlIOUtils.closeQuietlyMulti(template, bos, out, pdf);
                break;
            default:
        }
    }


    @Override
    public void printAllocateShippingList(Long id, HttpServletResponse response) throws IOException {
        response.setHeader("content-Type", "application/pdf");
        response.setHeader("Access-Control-Expose-Headers", "Content-Disposition");
        response.setHeader("Content-Disposition", "attachment;filename=printAllocateShippingList.pdf");
        AllocatePlanDo planDo = allocatePlanService.getById(id);
        String planNo = planDo.getPlanNo();
        byte[] planNoBarcode = BarCodeUtils.generateBarCode128(
                planNo,
                true,
                false,
                256,
                15,
                0.3f,
                0f,
                0f
        );
        ByteArrayInputStream planNoBarcodeImg = new ByteArrayInputStream(planNoBarcode);
        CompanyDo companyDo = companyService.getById(planDo.getCompanyId());
        LogisticsChannelDo logisticsChannelDo = logisticsChannelService.getById(planDo.getLogisticsChannelId());
        String trackingNumber = planDo.getTrackingNumber();
        String referenceNumber = planDo.getReferenceNumber();
        Date expectedArrivalDate = planDo.getExpectedArrivalDate();
        Date planShipmentTime = planDo.getPlanShipmentTime();
        AddressBookDo addressBookDo = addressBookService.getById(planDo.getAddressBookId());
        //通用参数
        Map<String, Object> data = new HashMap<>(32);
        data.put("planNo", planNo);
        data.put("planNoBarcode", Pictures.ofStream(planNoBarcodeImg, PictureType.PNG).size(240, 60).create());
        data.put("company", Objects.nonNull(companyDo) ? (companyDo.getName() + "(" + companyDo.getCompanyCode() + ")") : "");
        data.put("printTime", DateUtil.date());
        data.put("destinationType", planDo.getDestinationType().equals(1) ? "FBA" : "三方仓");
        data.put("logisticsChannelCode", Objects.nonNull(logisticsChannelDo) ? logisticsChannelDo.getChannelName() + "(" + logisticsChannelDo.getChannelCode() + ")" : "");
        data.put("trackingNumber", trackingNumber != null ? trackingNumber : "");
        data.put("referenceNumber", referenceNumber != null ? referenceNumber : "");
        data.put("expectedArrivalDate", expectedArrivalDate != null ? DateUtil.date(expectedArrivalDate) : "");
        data.put("planShipmentTime", planShipmentTime != null ? DateUtil.date(planShipmentTime) : "");
        data.put("address1", addressBookDo.getAddress1() != null ? addressBookDo.getAddress1() : "");
        data.put("address2", addressBookDo.getAddress2() != null ? addressBookDo.getAddress2() : "");
        data.put("houseNum", addressBookDo.getHouseNum() != null ? addressBookDo.getHouseNum() : "");
        List<AllocatePlanItemDo> planItemDoList = allocatePlanItemService.lambdaQuery()
                .eq(AllocatePlanItemDo::getOrderId, id)
                .eq(AllocatePlanItemDo::getDeleteFlag, SysConstant.DELETE_FLAG_FALSE)
                .list();
        Map<Long, ProductDo> productIdMatch = productService.lambdaQuery()
                .in(ProductDo::getId, planItemDoList.stream().map(AllocatePlanItemDo::getProductId).collect(Collectors.toList()))
                .list().stream().collect(Collectors.toMap(ProductDo::getId, val -> val));
        planItemDoList = new ArrayList<>(planItemDoList.stream().collect(Collectors.toMap(AllocatePlanItemDo::getProductId, val -> val, (v1, v2) -> {
            v1.setLockQty(v1.getLockQty() + v2.getLockQty());
            return v1;
        })).values());
        AtomicInteger i = new AtomicInteger(1);
        List<SkuShippingListTableDto> skuShippingListTableDtoList = new ArrayList<>();
        for (AllocatePlanItemDo planItemDo : planItemDoList) {
            ProductDo productDo = productIdMatch.get(planItemDo.getProductId());
            if (productDo == null) {
                continue;
            }
            SkuShippingListTableDto dto = new SkuShippingListTableDto();
            dto.setNo(String.valueOf(i.getAndIncrement()));
            dto.setJancode(productDo.getJanCode());
            dto.setSku(productDo.getSku());
            dto.setProductName(productDo.getProductName());
            dto.setQty(String.valueOf(planItemDo.getLockQty()));
            skuShippingListTableDtoList.add(dto);
        }
        data.put(
                "table",
                this.getSkuShippingListTableData(
                        skuShippingListTableDtoList,
                        new DynamicTableDto(0, 0, 0, 5, 1)
                )
        );
        data.put("skuKind", planItemDoList.size());
        data.put("skuTotalQty", planItemDoList.stream().map(AllocatePlanItemDo::getLockQty).reduce(0, Integer::sum));
        ConfigureBuilder configureBuilder = Configure.builder();
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ServletOutputStream out = response.getOutputStream();
        //模板配置
        configureBuilder.bind("table", new DynamicTablePolicy());
        XWPFTemplate template = XWPFTemplate.compile(this.getClass().getClassLoader().getResourceAsStream("wordTemplates/allocateShippingList.docx"), configureBuilder.build()).render(data);
        template.writeAndClose(bos);
        //转换为pdf
        ByteArrayOutputStream pdf = AsposePdfUtils.word2pdf(new ByteArrayInputStream(bos.toByteArray()));
        out.write(pdf.toByteArray());
        bos.flush();
        out.flush();
        PoitlIOUtils.closeQuietlyMulti(template, bos, out, pdf);
    }

    @Override
    public void printAllocatePickingList(Long id, HttpServletResponse response) throws Exception {
        response.setHeader("Content-Type", "application/pdf");
        response.setHeader("Access-Control-Expose-Headers", "Content-Disposition");
        response.setHeader("Content-Disposition", "attachment;filename=printAllocatePickingList.pdf");
        AllocatePlanDo planDo = allocatePlanService.getById(id);
        String planNo = planDo.getPlanNo();
        byte[] planNoBarcode = BarCodeUtils.generateBarCode128(
                planNo,
                true,
                false,
                256,
                15,
                0.3f,
                0f,
                0f
        );
        ByteArrayInputStream planNoBarcodeImg = null;
        if (planNoBarcode != null) {
            planNoBarcodeImg = new ByteArrayInputStream(planNoBarcode);
        }
        //获取客户信息
        CompanyDo companyDo = companyService.getById(planDo.getCompanyId());
        //通用参数
        Map<String, Object> data = new HashMap<>();
        data.put("planNo", planNo);
        data.put("company", Objects.nonNull(companyDo) ? companyDo.getName() + "(" + companyDo.getCompanyCode() + ")" : "");
        data.put("printTime", DateUtil.date());
        data.put("planNoBarcode", Objects.nonNull(planNoBarcodeImg) ? Pictures.ofStream(planNoBarcodeImg, PictureType.PNG).size(225, 60).create() : "-");
        List<AllocatePlanItemDo> planItemDoList = allocatePlanItemService.lambdaQuery()
                .eq(AllocatePlanItemDo::getOrderId, id)
                .list();
        if (CollUtil.isEmpty(planItemDoList)) {
            return;
        }
        List<OutboundPickingDo> pickingDoList = outboundPickingService.lambdaQuery()
                .eq(OutboundPickingDo::getOrderType, OutboundOrderKind.allocate.getValue())
                .in(OutboundPickingDo::getOutboundProductId, planItemDoList.stream().map(AllocatePlanItemDo::getId).collect(Collectors.toList()))
                .list();
        if (CollUtil.isEmpty(pickingDoList)) {
            return;
        }
        Map<Long, StorageLocationDo> rackIdMatch = storageLocationService.lambdaQuery()
                .in(StorageLocationDo::getId, pickingDoList.stream().map(OutboundPickingDo::getRackId).collect(Collectors.toList()))
                .list().stream().collect(Collectors.toMap(StorageLocationDo::getId, val -> val));
        ConfigureBuilder configureBuilder = Configure.builder();
        XWPFTemplate template;
        ByteArrayOutputStream pdf;
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ServletOutputStream out = response.getOutputStream();
        Map<Long, ProductDo> productIdMatch = productService.lambdaQuery()
                .in(ProductDo::getId, planItemDoList.stream().map(AllocatePlanItemDo::getProductId).collect(Collectors.toList()))
                .list().stream().collect(Collectors.toMap(ProductDo::getId, val -> val));
        AtomicInteger i = new AtomicInteger(1);
        List<TransferPickListBySkuTableDto> dtoList = new ArrayList<>();
        for (AllocatePlanItemDo planItemDo : planItemDoList) {
            ProductDo productDo = productIdMatch.get(planItemDo.getProductId());
            TransferPickListBySkuTableDto dto = new TransferPickListBySkuTableDto();
            dto.setNo(String.valueOf(i.getAndIncrement()));
            dto.setSku(productDo.getSku());
            dto.setJanCode(productDo.getJanCode());
            dto.setProductName(productDo.getProductName());
            List<OutboundPickingDo> pickingDoListBySku = pickingDoList.stream()
                    .filter(item -> item.getOutboundProductId().equals(planItemDo.getId()))
                    .collect(Collectors.toList());
            //合并相同库位相同状态的数据
            pickingDoListBySku.stream()
                    .collect(Collectors.toMap(k -> k.getRackId() + ":" + k.getPickFlag(), val -> val, (o1, o2) -> {
                        o1.setQty(o1.getQty() + o2.getQty());
                        return o1;
                    })).values().forEach(pickingDo -> {
                        HashMap<String, String> map = new HashMap<>(8);
                        map.put("cellCode", rackIdMatch.get(pickingDo.getRackId()).getCode());
                        map.put("qty", String.valueOf(pickingDo.getQty()));
                        map.put("state", pickingDo.getPickFlag() ? "Done" : "Not done");
                        dto.getSonList().add(map);
                    });
            dtoList.add(dto);
        }
        data.put(
                "table",
                this.getTransferPickListBySkuTableData(
                        dtoList,
                        new DynamicTableDto(0, 0, 3, 7, 1)
                )
        );
        data.put("skuKind", planItemDoList.size());
        data.put("skuTotalQty", planItemDoList.stream().map(AllocatePlanItemDo::getLockQty).reduce(0, Integer::sum));
        //模板配置
        configureBuilder.bind("table", new DynamicTablePolicy());
        template = XWPFTemplate.compile(this.getClass().getClassLoader().getResourceAsStream("wordTemplates/allocatePickList.docx"), configureBuilder.build()).render(data);
        template.writeAndClose(bos);
        //转换为pdf
        pdf = AsposePdfUtils.word2pdf(new ByteArrayInputStream(bos.toByteArray()));
        out.write(pdf.toByteArray());
        bos.flush();
        out.flush();
        PoitlIOUtils.closeQuietlyMulti(template, bos, out, pdf);
    }

    @Override
    public void allocateExportByFba(AllocatePlanQueryBo bo, HttpServletResponse response) throws IOException {
        // 设置下载的Excel名称，以当前时间为文件后缀
        String dateTime = DateUtil.format(DateUtil.date(), "yyyyMMddHHmmss");
        String fileName = "AllocateExportByFba_" + dateTime + ".xls";
        // 设置响应输出的头类型
        response.setHeader("content-Type", "application/vnd.ms-excel");
        response.setHeader("Access-Control-Expose-Headers", "Content-Disposition");
        response.setHeader("Content-Disposition", "attachment;filename=" + fileName);
        List<AllocatePlanBatchVo> list = allocatePlanDao.getListPage(bo);
        if (CollUtil.isEmpty(list)) {
            throw new BizException(SysConstant.No_Data);
        }
        ExportParams sheet1Params = new ExportParams();
        //设置样式
        sheet1Params.setStyle(ExcelExportTitleStyle.class);
        // 设置sheet的名称
        sheet1Params.setSheetName("fba_allocation_plan");
        //fba调拨计划
        Map<String, Object> allocateByFbaExportList = new HashMap<>(4);
        Map<Long, CompanyDo> companyIdMatch = companyService.lambdaQuery()
                .in(CompanyDo::getId, list.stream().map(AllocatePlanBatchVo::getCompanyId).collect(Collectors.toSet()))
                .list().stream().collect(Collectors.toMap(CompanyDo::getId, val -> val));
        Map<Long, WarehouseDo> whIdMatch = warehouseService.lambdaQuery()
                .in(WarehouseDo::getId, list.stream().map(AllocatePlanBatchVo::getDeliveryWhId).collect(Collectors.toSet()))
                .list().stream().collect(Collectors.toMap(WarehouseDo::getId, val -> val));
        Map<Long, AddressBookDo> addressIdMatch = addressBookService.lambdaQuery()
                .in(AddressBookDo::getId, list.stream().map(AllocatePlanBatchVo::getAddressBookId).collect(Collectors.toSet())).list().stream().collect(Collectors.toMap(AddressBookDo::getId, val -> val));
        Set<Long> channelIdList = list.stream().map(AllocatePlanBatchVo::getLogisticsChannelId).collect(Collectors.toSet());
        Map<Long, LogisticsChannelDo> lcIdMatch = new HashMap<>();
        if (CollUtil.isNotEmpty(channelIdList)) {
            lcIdMatch = logisticsChannelService.lambdaQuery()
                    .in(LogisticsChannelDo::getId, channelIdList).list().stream().collect(Collectors.toMap(LogisticsChannelDo::getId, val -> val));
        }
        List<AllocatePlanItemDo> planItemAllList = allocatePlanItemService.lambdaQuery()
                .in(AllocatePlanItemDo::getOrderId, list.stream().map(AllocatePlanBatchVo::getBatchId).collect(Collectors.toList()))
                .eq(AllocatePlanItemDo::getDeleteFlag, SysConstant.DELETE_FLAG_FALSE)
                .list();
        Map<Long, List<AllocatePlanItemDo>> planIdMatchList = planItemAllList.stream().collect(Collectors.groupingBy(AllocatePlanItemDo::getOrderId));
        List<AllocateOutExportByFbaDto> dtoList = new ArrayList<>();
        for (AllocatePlanBatchVo vo : list) {
//            AllocateOutExportByFbaDto dto = new AllocateOutExportByFbaDto();
//            BeanUtils.copyProperties(vo, dto);
            AllocateOutExportByFbaDto dto = BeanConvert.INSTANCE.allocateVoToAllocateOutExportByFbaDto(vo);
            CompanyDo companyDo = companyIdMatch.get(vo.getCompanyId());
            if (companyDo == null) {
                dto.setCompanyNameCode("");
            } else {
                dto.setCompanyNameCode(companyDo.getName() + "(" + companyDo.getCompanyCode() + ")");
            }
            WarehouseDo warehouseDo = whIdMatch.get(vo.getDeliveryWhId());
            if (warehouseDo == null) {
                dto.setDeliveryWarehouse("");
            } else {
                dto.setDeliveryWarehouse(warehouseDo.getWhNameCn() + "(" + warehouseDo.getWhCode() + ")");
            }
            dto.setStatus(
                    "oms".equalsIgnoreCase(bo.getSource())
                            ? AllocatePlanStatus.getOmsDesc(vo.getStatus())
                            : AllocatePlanStatus.getDescByVal(vo.getStatus())
            );
            LogisticsChannelDo channelDo = lcIdMatch.get(vo.getLogisticsChannelId());
            if (Objects.nonNull(channelDo)) {
                dto.setShippingService(channelDo.getChannelCode());
            }/* else {
                dto.setShippingService("自动选取价格最低的物流渠道");
            }*/
            AddressBookDo addressBookDo = addressIdMatch.get(vo.getAddressBookId());
            if (Objects.nonNull(addressBookDo)) {
//                BeanUtils.copyProperties(addressBookDo, dto);
                BeanConvert.INSTANCE.addressBookDoToAllocateOutExportByFbaDto(addressBookDo, dto);
            }
            //出库产品列表
            List<AllocatePlanItemDo> planItemList = planIdMatchList.get(vo.getBatchId());
            Map<Long, List<AllocatePlanItemDo>> platProductIdGroup = planItemList.stream().collect(Collectors.groupingBy(AllocatePlanItemDo::getPlatformProductId));
            List<AllocateOutExportByFbaItemDto> childList = new ArrayList<>();
            for (Map.Entry<Long, List<AllocatePlanItemDo>> entry : platProductIdGroup.entrySet()) {
                List<AllocatePlanItemDo> itemDoList = entry.getValue();
                AllocatePlanItemDo planItemDo = itemDoList.get(0);
//                AllocateOutExportByFbaItemDto child = new AllocateOutExportByFbaItemDto();
//                BeanUtils.copyProperties(planItemDo, child);
                AllocateOutExportByFbaItemDto child = BeanConvert.INSTANCE.allocateItemDoToAllocateOutExportByFbaItemDto(planItemDo);
                child.setPairProductItemList(JSONUtil.toList(JSONUtil.toJsonStr(itemDoList), PairProductItemDto.class));
                childList.add(child);
            }
            dto.setChildList(childList);
            dtoList.add(dto);
        }
        // title的参数为ExportParams类型，目前仅仅在ExportParams中设置了sheetName
        allocateByFbaExportList.put("title", sheet1Params);
        // 模版导出对应得实体类型，即包含了List的对象
        allocateByFbaExportList.put("entity", AllocateOutExportByFbaDto.class);
        // sheet中要填充得数据
        allocateByFbaExportList.put("data", dtoList);
        //添加表
        List<Map<String, Object>> sheetsList = new ArrayList<>(2);
        sheetsList.add(allocateByFbaExportList);
        //创建excel文件的方法
        Workbook workbook = ExcelExportUtil.exportExcel(sheetsList, ExcelType.HSSF);
        //通过response输出流直接输入给客户端
        ServletOutputStream outputStream = response.getOutputStream();
        workbook.write(outputStream);
        outputStream.flush();
        outputStream.close();
    }

    @Override
    public void allocateExportBy3rdWh(AllocatePlanQueryBo bo, HttpServletResponse response) throws IOException {
        // 设置下载的Excel名称，以当前时间为文件后缀
        String dateTime = DateUtil.format(DateUtil.date(), "yyyyMMddHHmmss");
        String fileName = "AllocateExportBy3rdWh_" + dateTime + ".xls";
        // 设置响应输出的头类型
        response.setHeader("content-Type", "application/vnd.ms-excel");
        response.setHeader("Access-Control-Expose-Headers", "Content-Disposition");
        response.setHeader("Content-Disposition", "attachment;filename=" + fileName);
        List<AllocatePlanBatchVo> list = allocatePlanDao.getListPage(bo);
        if (CollUtil.isEmpty(list)) {
            throw new BizException(SysConstant.No_Data);
        }
        ExportParams sheet1Params = new ExportParams();
        //设置样式
        sheet1Params.setStyle(ExcelExportTitleStyle.class);
        // 设置sheet的名称
        sheet1Params.setSheetName("tripartite_warehouse_allocation_plan");
        //fba调拨计划
        Map<String, Object> allocateBy3rdWhExportList = new HashMap<>(4);
        Map<Long, CompanyDo> companyIdMatch = companyService.lambdaQuery()
                .in(CompanyDo::getId, list.stream().map(AllocatePlanBatchVo::getCompanyId).collect(Collectors.toSet()))
                .list().stream().collect(Collectors.toMap(CompanyDo::getId, val -> val));
        Map<Long, WarehouseDo> whIdMatch = warehouseService.lambdaQuery()
                .in(WarehouseDo::getId, list.stream().map(AllocatePlanBatchVo::getDeliveryWhId).collect(Collectors.toSet()))
                .list().stream().collect(Collectors.toMap(WarehouseDo::getId, val -> val));
        Map<Long, AddressBookDo> addressIdMatch = addressBookService.lambdaQuery()
                .in(AddressBookDo::getId, list.stream().map(AllocatePlanBatchVo::getAddressBookId).collect(Collectors.toSet())).list().stream().collect(Collectors.toMap(AddressBookDo::getId, val -> val));
        Set<Long> channelIdList = list.stream().map(AllocatePlanBatchVo::getLogisticsChannelId).collect(Collectors.toSet());
        Map<Long, LogisticsChannelDo> lcIdMatch = new HashMap<>();
        if (CollUtil.isNotEmpty(channelIdList)) {
            lcIdMatch = logisticsChannelService.lambdaQuery()
                    .in(LogisticsChannelDo::getId, channelIdList).list().stream().collect(Collectors.toMap(LogisticsChannelDo::getId, val -> val));
        }
        List<AllocatePlanItemDo> planItemAllList = allocatePlanItemService.lambdaQuery()
                .in(AllocatePlanItemDo::getOrderId, list.stream().map(AllocatePlanBatchVo::getBatchId).collect(Collectors.toList()))
                .eq(AllocatePlanItemDo::getDeleteFlag, SysConstant.DELETE_FLAG_FALSE)
                .list();
        Map<Long, List<AllocatePlanItemDo>> planIdMatchList = planItemAllList.stream().collect(Collectors.groupingBy(AllocatePlanItemDo::getOrderId));
        List<AllocateOutExportBy3WhDto> dtoList = new ArrayList<>();
        for (AllocatePlanBatchVo vo : list) {
//            AllocateOutExportBy3WhDto dto = new AllocateOutExportBy3WhDto();
//            BeanUtils.copyProperties(vo, dto);
            AllocateOutExportBy3WhDto dto = BeanConvert.INSTANCE.allocateVoToAllocateOutExportBy3WhDto(vo);
            CompanyDo companyDo = companyIdMatch.get(vo.getCompanyId());
            if (companyDo == null) {
                dto.setCompanyNameCode("");
            } else {
                dto.setCompanyNameCode(companyDo.getName() + "(" + companyDo.getCompanyCode() + ")");
            }
            WarehouseDo warehouseDo = whIdMatch.get(vo.getDeliveryWhId());
            if (warehouseDo == null) {
                dto.setDeliveryWarehouse("");
            } else {
                dto.setDeliveryWarehouse(warehouseDo.getWhNameCn() + "(" + warehouseDo.getWhCode() + ")");
            }
            dto.setStatus(
                    "oms".equalsIgnoreCase(bo.getSource())
                            ? AllocatePlanStatus.getOmsDesc(vo.getStatus())
                            : AllocatePlanStatus.getDescByVal(vo.getStatus())
            );
            LogisticsChannelDo channelDo = lcIdMatch.get(vo.getLogisticsChannelId());
            if (Objects.nonNull(channelDo)) {
                dto.setShippingService(channelDo.getChannelCode());
            }/* else {
                dto.setShippingService("自动选取价格最低的物流渠道");
            }*/
            AddressBookDo addressBookDo = addressIdMatch.get(vo.getAddressBookId());
            if (Objects.nonNull(addressBookDo)) {
//                BeanUtils.copyProperties(addressBookDo, dto);
                BeanConvert.INSTANCE.addressBookDoToAllocateOutExportBy3WhDto(addressBookDo, dto);
            }
            //出库产品列表
            List<AllocatePlanItemDo> planItemList = planIdMatchList.get(vo.getBatchId());
            AllocatePlanItemDo planItemDo = planItemList.get(0);
//            BeanUtils.copyProperties(planItemDo, dto);
            BeanConvert.INSTANCE.planItemDoToAllocateOutExportBy3WhDto(planItemDo, dto);
            dto.setPairProductItemList(JSONUtil.toList(JSONUtil.toJsonStr(planItemList), PairProductItemDto.class));
            dtoList.add(dto);
        }
        // title的参数为ExportParams类型，目前仅仅在ExportParams中设置了sheetName
        allocateBy3rdWhExportList.put("title", sheet1Params);
        // 模版导出对应得实体类型，即包含了List的对象
        allocateBy3rdWhExportList.put("entity", AllocateOutExportBy3WhDto.class);
        // sheet中要填充得数据
        allocateBy3rdWhExportList.put("data", dtoList);
        //添加表
        List<Map<String, Object>> sheetsList = new ArrayList<>(2);
        sheetsList.add(allocateBy3rdWhExportList);
        //创建excel文件的方法
        Workbook workbook = ExcelExportUtil.exportExcel(sheetsList, ExcelType.HSSF);
        //通过response输出流直接输入给客户端
        ServletOutputStream outputStream = response.getOutputStream();
        workbook.write(outputStream);
        outputStream.flush();
        outputStream.close();
    }

    @Override
    public void getDeliveryOrderZip(Long transferId, HttpServletResponse response) throws IOException {
        ZipOutputStream zos = new ZipOutputStream(response.getOutputStream());
        zos.setLevel(1);
        response.setHeader("Content-Type", "application/x-zip-compressed");
        response.setHeader("Access-Control-Expose-Headers", "Content-Disposition");
        response.setHeader("Content-Disposition", "attachment;filename=DeliveryOrder_" + transferId + ".zip");
        response.setCharacterEncoding("Shift-JIS");
        //导出数据
        ByteArrayOutputStream xlsBos = null;
        ByteArrayOutputStream pdfBos = null;
        try {
            xlsBos = getDeliveryOrderExcelOutputStream(transferId);
            if (xlsBos == null) {
                throw new BizException(SysConstant.No_Data);
            }
            pdfBos = AsposePdfUtils.excel2pdf(new ByteArrayInputStream(xlsBos.toByteArray()));
            zos.putNextEntry(new ZipEntry("DeliveryOrder_" + transferId + ".xls"));
            xlsBos.writeTo(zos);
            zos.closeEntry();
            zos.putNextEntry(new ZipEntry("DeliveryOrder_" + transferId + ".pdf"));
            pdfBos.writeTo(zos);
            zos.closeEntry();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (xlsBos != null) {
                xlsBos.close();
            }
            if (pdfBos != null) {
                pdfBos.close();
            }
            zos.finish();
            zos.close();
        }
    }

    @Override
    public void getDeliveryOrderBatch(List<Long> ids, HttpServletResponse response) throws IOException {
        if (CollUtil.isEmpty(ids)) {
            throw new BizException(SysConstant.No_Data);
        }
        if (ids.size() > 100) {
            throw new BizException(SysConstant.Batch_Over_100);
        }
        ZipOutputStream zos = new ZipOutputStream(response.getOutputStream());
        zos.setLevel(1);
        response.setHeader("Content-Type", "application/x-zip-compressed");
        response.setHeader("Access-Control-Expose-Headers", "Content-Disposition");
        response.setHeader("Content-Disposition", "attachment;filename=DeliveryOrder_Batch.zip");
        response.setCharacterEncoding("Shift-JIS");
        try (ByteArrayOutputStream totalPdfBos = new ByteArrayOutputStream()) {
            Document document = new Document(PageSize.A4);
            PdfWriter writer = PdfWriter.getInstance(document, totalPdfBos);
            document.open();
            for (Long id : ids) {
                //按照模板生成excel文件
                ByteArrayOutputStream xlsBos = getDeliveryOrderExcelOutputStream(id);
                if (xlsBos == null) {
                    throw new BizException(SysConstant.Excel_Template_Error);
                }
                //excel文件转pdf
                ByteArrayOutputStream pdfBos = AsposePdfUtils.excel2pdf(new ByteArrayInputStream(xlsBos.toByteArray()));
                //填入压缩包
                zos.putNextEntry(new ZipEntry("DeliveryOrder_" + id + ".xls"));
                xlsBos.writeTo(zos);
                zos.closeEntry();
                //关闭excel流
                xlsBos.close();
                //读取pdf文件，将文件内容填入主pdf文件
                PdfReader reader = new PdfReader(pdfBos.toByteArray());
                int totalPages = reader.getNumberOfPages();
                for (int pageNumber = 1; pageNumber <= totalPages; pageNumber++) {
                    document.newPage();
                    writer.getDirectContent().addTemplate(writer.getImportedPage(reader, pageNumber), 0, 0);
                }
                //关闭pdf文件
                pdfBos.close();
            }
            document.close();
            writer.flush();
            writer.close();
            zos.putNextEntry(new ZipEntry("DeliveryOrder_Batch.pdf"));
            totalPdfBos.writeTo(zos);
            zos.closeEntry();
        } catch (Exception e) {
            log.error(e.getMessage());
            throw new BizException(SysConstant.Excel_Template_Error);
        } finally {
            zos.finish();
            zos.close();
        }
    }

    @Override
    public ByteArrayOutputStream getDeliveryOrderExcelOutputStream(Long transferId) {
        String templatePath = "excelTemplates/TransferDeliveryOrder.xls";
        if ("kk".equals(customer)) {
            templatePath = "excelTemplates/TransferDeliveryOrder_kk.xls";
        }
        TemplateExportParams templateExportParams = new TemplateExportParams(templatePath, false);
        //导出数据
        try (Workbook book = ExcelExportUtil.exportExcel(templateExportParams, this.buildTransferDeliveryOrderTemplateMap(transferId))) {
            ByteArrayOutputStream xlsBos = new ByteArrayOutputStream();
            book.write(xlsBos);
            return xlsBos;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    @Override
    public ByteArrayOutputStream getDeliveryOrderPdfOutputStream(Long transferId) {
        ByteArrayOutputStream xlsBos = getDeliveryOrderExcelOutputStream(transferId);
        if (xlsBos == null) {
            throw new BizException(SysConstant.No_Data);
        }
        return AsposePdfUtils.excel2pdf(new ByteArrayInputStream(xlsBos.toByteArray()));
    }

    private Map<String, Object> buildTransferDeliveryOrderTemplateMap(Long transferId) {
        OrderOutboundDo outboundDo = outboundService.getById(transferId);
        if (Objects.isNull(outboundDo)) {
            throw new BizException(SysConstant.Outbound_Order_Does_Not_Exist);
        }
        List<OrderOutboundProductDo> outboundProductDoList = outboundProductService.lambdaQuery()
                .eq(OrderOutboundProductDo::getOutboundId, outboundDo.getId())
                .list();
        if (CollUtil.isEmpty(outboundProductDoList)) {
            throw new BizException(SysConstant.Order_No_Product);
        }
        Set<Long> productIdSet = outboundProductDoList.stream().map(OrderOutboundProductDo::getProductId).collect(Collectors.toSet());
        Map<Long, ProductDo> productIdMatch = new HashMap<>();
        if (CollUtil.isNotEmpty(productIdSet)) {
            productIdMatch = productService.lambdaQuery()
                    .in(ProductDo::getId, productIdSet)
                    .list().stream().collect(Collectors.toMap(ProductDo::getId, val -> val));
        }
        Integer outboundMode = outboundDo.getOutboundMode();
        Map<String, Object> map = new HashMap<>(16);
        map.put("receiver", outboundDo.getReceiver() + "  様");
        map.put("no", (StrUtil.isEmpty(outboundDo.getReferenceNumber())) ? outboundDo.getOutboundNo() : outboundDo.getReferenceNumber());
        map.put("outboundMode", outboundMode);
        map.put("sendDate", Objects.nonNull(outboundDo.getOutboundTime()) ? DateUtil.format(outboundDo.getOutboundTime(), "yyyy-MM-dd HH:mm:ss") : "");
        map.put("deliveryDate", outboundDo.getDeliveryDate());
        map.put("postCode", outboundDo.getPostCode());
        String address = (StrUtil.isBlank(outboundDo.getProvinceName()) ? "" : outboundDo.getProvinceName()) + (StrUtil.isBlank(outboundDo.getCityName()) ? "" : outboundDo.getCityName()) + (StrUtil.isBlank(outboundDo.getAddress1()) ? "" : outboundDo.getAddress1()) + (StrUtil.isBlank(outboundDo.getAddress2()) ? "" : outboundDo.getAddress2()) + (StrUtil.isBlank(outboundDo.getHouseNum()) ? "" : outboundDo.getHouseNum());
        map.put("address", address);
        map.put("phone", outboundDo.getPhone());
        String remark = (StrUtil.isBlank(outboundDo.getRemark()) ? "" : outboundDo.getRemark()) + (StrUtil.isBlank(outboundDo.getRemark2()) ? "" : outboundDo.getRemark2());
        map.put("remark", remark);
        List<Map<String, Object>> skuList = new ArrayList<>(outboundProductDoList.size());
        int totalSkuQty = 0;
        if (outboundMode == 1) {
            for (OrderOutboundProductDo outboundProductDo : outboundProductDoList) {
                ProductDo productDo = productIdMatch.get(outboundProductDo.getProductId());
                if (Objects.isNull(productDo)) {
                    continue;
                }
                Map<String, Object> skuMap = new HashMap<>(16);
                skuMap.put("title", productDo.getProductName());
                skuMap.put("janCode", productDo.getJanCode());
                skuMap.put("itfCode", productDo.getItf());
                skuMap.put("sku", productDo.getSku());
                Integer qty = outboundProductDo.getForecastQty();
                totalSkuQty += qty;
                skuMap.put("qty", qty);
                int boxQty = 0;
                int boxQty1 = qty;
                //产品存在内装数
                if (Objects.nonNull(productDo.getBuiltInNumber()) && productDo.getBuiltInNumber() > 0) {
                    //ケース
                    boxQty = outboundProductDo.getForecastQty() / productDo.getBuiltInNumber();
                    //個
                    boxQty1 = outboundProductDo.getForecastQty() % productDo.getBuiltInNumber();
                }
                skuMap.put("boxQty", (boxQty == 0 ? "" : boxQty + "ケース") + (boxQty1 == 0 ? "" : boxQty1 + "個"));
                skuList.add(skuMap);
            }
        } else if (outboundMode == 2) {
            Map<String, List<OrderOutboundProductDo>> boxNoMatchList = outboundProductDoList.stream().collect(Collectors.groupingBy(OrderOutboundProductDo::getBoxTypeNumber));
            for (Map.Entry<String, List<OrderOutboundProductDo>> entry : boxNoMatchList.entrySet()) {
                String boxTypeNo = entry.getKey();
                List<OrderOutboundProductDo> value = entry.getValue();
                ProductDo productDo = productIdMatch.get(value.get(0).getProductId());
                if (Objects.isNull(productDo)) {
                    continue;
                }
                Map<String, Object> boxMap = new HashMap<>(16);
                boxMap.put("title", boxTypeNo);
                boxMap.put("janCode", value.size() > 1 ? "雑貨" : productDo.getJanCode());
                boxMap.put("itfCode", value.size() > 1 ? "雑貨" : productDo.getItf());
                boxMap.put("sku", value.size() > 1 ? "雑貨" : productDo.getSku());
                int qty = value.stream().mapToInt(OrderOutboundProductDo::getForecastQty).sum();
                totalSkuQty += qty;
                boxMap.put("qty", qty);
                Integer boxQty = value.get(0).getForecastBoxQty();
                boxMap.put("boxQty", boxQty + "ケース");
                skuList.add(boxMap);
            }
        } else {
            throw new BizException(SysConstant.Outbound_Mode_Error);
        }
        map.put("skuList", skuList);
        map.put("totalQty", totalSkuQty);
        return map;
    }

    @Override
    public void getNormalOutboundImportTemplate1(HttpServletResponse response, Long tenantId, Long companyId) throws IOException {
        TemplateExportParams params = new TemplateExportParams(
                "excelTemplates/NormalOutbound_Import_col.xlsx", true);
        response.setCharacterEncoding("UTF-8");
        response.setHeader("content-Type", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
        response.setHeader("Access-Control-Expose-Headers", "Content-Disposition");
        response.setHeader("Content-Disposition", "attachment;filename=NormalOutbound_Import_1.xlsx");
        //导出数据
        try (Workbook book = ExcelExportUtil.exportExcel(params, this.buildOutboundTemplateSheetMap(tenantId, companyId))) {
            book.write(response.getOutputStream());
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            response.getOutputStream().flush();
            response.getOutputStream().close();
        }
    }

    @Override
    public void getNormalOutboundImportTemplate2(HttpServletResponse response, Long tenantId, Long companyId) throws IOException {
        TemplateExportParams params = new TemplateExportParams(
                "excelTemplates/NormalOutbound_Import_row.xlsx", true);
        response.setCharacterEncoding("UTF-8");
        response.setHeader("content-Type", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
        response.setHeader("Access-Control-Expose-Headers", "Content-Disposition");
        response.setHeader("Content-Disposition", "attachment;filename=NormalOutbound_Import_2.xlsx");
        //导出数据
        try (Workbook book = ExcelExportUtil.exportExcel(params, this.buildOutboundTemplateSheetMap(tenantId, companyId))) {
            book.write(response.getOutputStream());
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            response.getOutputStream().flush();
            response.getOutputStream().close();
        }
    }

    /**
     * 构建导入更新跟踪号模板数据
     */
    public Map<String, Object> buildUpdateTrackingNumberTemplateSheetMap() {
        //每个sheet的数据对应一个map
        Map<String, Object> sheetMap = new HashMap<>(4);
        //处理第一个sheet(carrier承运商)
        List<Map<String, String>> carrierList = StaticDict.Lc_Carrier.getCarrierList();
        sheetMap.put("carrierList", carrierList);
        return sheetMap;
    }

    /**
     * 构建导入出库单模板数据
     *
     * @param tenantId 租户id
     */
    public Map<String, Object> buildOutboundTemplateSheetMap(Long tenantId, Long companyId) {
        //每个sheet的数据对应一个map
        Map<String, Object> sheetMap = new HashMap<>(16);
        //处理第一个sheet(Warehouse仓库信息)
        List<WarehouseDo> warehouseDoList;
        if (Objects.nonNull(companyId)) {
            warehouseDoList = warehouseService.getWarehouseCompanyDo(companyId, tenantId);
        } else {
            warehouseDoList = warehouseService.lambdaQuery()
                    .eq(WarehouseDo::getTenantId, tenantId)
                    .eq(WarehouseDo::getStatus, 0)
                    .list();
        }
        List<Map<String, String>> whList = new ArrayList<>();
        for (WarehouseDo warehouseDo : warehouseDoList) {
            Map<String, String> whMap = new HashMap<>();
            whMap.put("warehouseCode", warehouseDo.getWhCode());
            whMap.put("warehouseName", warehouseDo.getWhNameCn());
            whList.add(whMap);
        }
        sheetMap.put("whList", whList);
        //处理第二个sheet(CountryCodeOfOrigin原产地简码)
        List<Map<String, String>> countryEnumList = CountryEnum.getCountryEnumList();
        sheetMap.put("countryList", countryEnumList);
        //处理第三个sheet(LogisticsChannel物流渠道)
        List<LogisticsChannelDo> channelDoList = logisticsChannelService.lambdaQuery().eq(LogisticsChannelDo::getTenantId, tenantId).list();
        Map<Long, List<LcShippingStorageDo>> channelIdMatchWhIdList = shippingStorageService.list().stream().collect(Collectors.groupingBy(LcShippingStorageDo::getChannelId));
        List<Map<String, String>> channelList = new ArrayList<>();
        for (LogisticsChannelDo channelDo : channelDoList) {
            List<LcShippingStorageDo> lcShippingStorageDoList = channelIdMatchWhIdList.get(channelDo.getId());
            Map<String, String> channelMap = new HashMap<>();
            channelMap.put("name", channelDo.getChannelName());
            channelMap.put("code", channelDo.getChannelCode());
            if (CollUtil.isNotEmpty(lcShippingStorageDoList)) {
                List<Long> whIdList = lcShippingStorageDoList.stream().map(LcShippingStorageDo::getStorageId).collect(Collectors.toList());
                channelMap.put("bindWh", warehouseDoList.stream().filter(warehouseDo -> whIdList.contains(warehouseDo.getId())).map(WarehouseDo::getWhCode).collect(Collectors.joining(",")));
            } else {
                channelMap.put("bindWh", "");
            }
            channelList.add(channelMap);
        }
        sheetMap.put("lcList", channelList);
        //处理第四个sheet(carrier承运商)
        List<Map<String, String>> carrierList = StaticDict.Lc_Carrier.getCarrierList();
        sheetMap.put("carrierList", carrierList);
        //处理第五个sheet(SalesPlatform销售平台)
        ArrayList<CommonKvVo> salesPlatformList = SalesPlatformEnum.getSalesPlatformList();
        sheetMap.put("salesPlatformList", salesPlatformList);
        return sheetMap;
    }

    public Map<String, Object> buildFbaAllocateTemplateSheetMap(Long tenantId, Long companyId) {
        //每个sheet的数据对应一个map
        Map<String, Object> sheetMap = new HashMap<>(16);
        //处理第一个sheet(Warehouse仓库信息)
        List<WarehouseDo> warehouseDoList;
        if (Objects.nonNull(companyId)) {
            warehouseDoList = warehouseService.getWarehouseCompanyDo(companyId, tenantId);
        } else {
            warehouseDoList = warehouseService.lambdaQuery()
                    .eq(WarehouseDo::getTenantId, tenantId)
                    .eq(WarehouseDo::getStatus, 0)
                    .list();
        }
        List<Map<String, String>> whList = new ArrayList<>();
        for (WarehouseDo warehouseDo : warehouseDoList) {
            Map<String, String> whMap = new HashMap<>();
            whMap.put("warehouseCode", warehouseDo.getWhCode());
            whMap.put("warehouseName", warehouseDo.getWhNameCn());
            whList.add(whMap);
        }
        sheetMap.put("whList", whList);
        //处理第二个sheet(FbaAddress仓库地址)
        List<AddressBookDo> addressBookDoList = addressBookService.lambdaQuery()
                .eq(AddressBookDo::getTenantId, tenantId)
                .eq(AddressBookDo::getCompanyId, companyId)
                .eq(AddressBookDo::getAddressType, AddressBookTypeEnum.one.getValue())
                .list();
        List<Map<String, String>> fbaAddressList = new ArrayList<>();
        for (AddressBookDo addressBookDo : addressBookDoList) {
            Map<String, String> addressMap = new HashMap<>();
            addressMap.put("id", String.valueOf(addressBookDo.getId()));
            addressMap.put("name", addressBookDo.getName());
            addressMap.put("receiver", addressBookDo.getReceiver());
            addressMap.put("postCode", addressBookDo.getPostCode());
            addressMap.put("countryRegionCode", addressBookDo.getCountryRegionCode());
            addressMap.put("address1", addressBookDo.getAddress1());
            fbaAddressList.add(addressMap);
        }
        sheetMap.put("fbaAddressList", fbaAddressList);
        //处理第三个sheet(LogisticsChannel物流渠道)
        List<LogisticsChannelDo> channelDoList = logisticsChannelService.lambdaQuery().eq(LogisticsChannelDo::getTenantId, tenantId).list();
        Map<Long, List<LcShippingStorageDo>> channelIdMatchWhIdList = shippingStorageService.list().stream().collect(Collectors.groupingBy(LcShippingStorageDo::getChannelId));
        List<Map<String, String>> channelList = new ArrayList<>();
        for (LogisticsChannelDo channelDo : channelDoList) {
            List<LcShippingStorageDo> lcShippingStorageDoList = channelIdMatchWhIdList.get(channelDo.getId());
            Map<String, String> channelMap = new HashMap<>();
            channelMap.put("name", channelDo.getChannelName());
            channelMap.put("code", channelDo.getChannelCode());
            if (CollUtil.isNotEmpty(lcShippingStorageDoList)) {
                List<Long> whIdList = lcShippingStorageDoList.stream().map(LcShippingStorageDo::getStorageId).collect(Collectors.toList());
                channelMap.put("bindWh", warehouseDoList.stream().filter(warehouseDo -> whIdList.contains(warehouseDo.getId())).map(WarehouseDo::getWhCode).collect(Collectors.joining(",")));
            } else {
                channelMap.put("bindWh", "");
            }
            channelList.add(channelMap);
        }
        sheetMap.put("lcList", channelList);
        //处理第四个sheet(FBAStoreCode店铺编码)
        List<PlatformStoreDo> platformStoreDoList = platformStoreService.lambdaQuery().eq(PlatformStoreDo::getPlatformCode, "amazon").eq(Objects.nonNull(companyId), PlatformStoreDo::getCompanyId, companyId).eq(Objects.nonNull(tenantId), PlatformStoreDo::getTenantId, tenantId).list();
        List<Map<String, String>> platStoreList = new ArrayList<>();
        for (PlatformStoreDo platformStoreDo : platformStoreDoList) {
            Map<String, String> storeMap = new HashMap<>();
            storeMap.put("storeName", platformStoreDo.getStoreName());
            storeMap.put("storeCode", platformStoreDo.getStoreCode());
            platStoreList.add(storeMap);
        }
        sheetMap.put("platStoreList", platStoreList);
        return sheetMap;
    }

    public Map<String, Object> build3rdWhAllocateTemplateSheetMap(Long tenantId, Long companyId) {
        //每个sheet的数据对应一个map
        Map<String, Object> sheetMap = new HashMap<>(16);
        //处理第一个sheet(Warehouse仓库信息)
        List<WarehouseDo> warehouseDoList;
        if (Objects.nonNull(companyId)) {
            warehouseDoList = warehouseService.getWarehouseCompanyDo(companyId, tenantId);
        } else {
            warehouseDoList = warehouseService.lambdaQuery()
                    .eq(WarehouseDo::getTenantId, tenantId)
                    .eq(WarehouseDo::getStatus, 0)
                    .list();
        }
        List<Map<String, String>> whList = new ArrayList<>();
        for (WarehouseDo warehouseDo : warehouseDoList) {
            Map<String, String> whMap = new HashMap<>();
            whMap.put("warehouseCode", warehouseDo.getWhCode());
            whMap.put("warehouseName", warehouseDo.getWhNameCn());
            whList.add(whMap);
        }
        sheetMap.put("whList", whList);
        //处理第二个sheet(DestinationAddress目的地编码)
        List<AddressBookDo> addressBookDoList = addressBookService.lambdaQuery()
                .eq(AddressBookDo::getTenantId, tenantId)
                .eq(AddressBookDo::getCompanyId, companyId)
                .eq(AddressBookDo::getAddressType, AddressBookTypeEnum.zero.getValue())
                .list();
        List<Map<String, String>> fbaAddressList = new ArrayList<>();
        for (AddressBookDo addressBookDo : addressBookDoList) {
            Map<String, String> addressMap = new HashMap<>();
            addressMap.put("id", String.valueOf(addressBookDo.getId()));
            addressMap.put("name", addressBookDo.getName());
            addressMap.put("receiver", addressBookDo.getReceiver());
            addressMap.put("postCode", addressBookDo.getPostCode());
            addressMap.put("countryRegionCode", addressBookDo.getCountryRegionCode());
            addressMap.put("address1", addressBookDo.getAddress1());
            fbaAddressList.add(addressMap);
        }
        sheetMap.put("fbaAddressList", fbaAddressList);
        //处理第三个sheet(LogisticsChannel物流渠道)
        List<LogisticsChannelDo> channelDoList = logisticsChannelService.lambdaQuery().eq(LogisticsChannelDo::getTenantId, tenantId).list();
        Map<Long, List<LcShippingStorageDo>> channelIdMatchWhIdList = shippingStorageService.list().stream().collect(Collectors.groupingBy(LcShippingStorageDo::getChannelId));
        List<Map<String, String>> channelList = new ArrayList<>();
        for (LogisticsChannelDo channelDo : channelDoList) {
            List<LcShippingStorageDo> lcShippingStorageDoList = channelIdMatchWhIdList.get(channelDo.getId());
            Map<String, String> channelMap = new HashMap<>();
            channelMap.put("name", channelDo.getChannelName());
            channelMap.put("code", channelDo.getChannelCode());
            if (CollUtil.isNotEmpty(lcShippingStorageDoList)) {
                List<Long> whIdList = lcShippingStorageDoList.stream().map(LcShippingStorageDo::getStorageId).collect(Collectors.toList());
                channelMap.put("bindWh", warehouseDoList.stream().filter(warehouseDo -> whIdList.contains(warehouseDo.getId())).map(WarehouseDo::getWhCode).collect(Collectors.joining(",")));
            } else {
                channelMap.put("bindWh", "");
            }
            channelList.add(channelMap);
        }
        sheetMap.put("lcList", channelList);
        return sheetMap;
    }

    @Override
    public CommonBatchOperationVo importNormalOutbound1(MultipartFile file, Long tenantId, Long userId, String username, Long companyId) {
        CommonBatchOperationVo res = new CommonBatchOperationVo();
        List<NormalOutImportByColDto> importDtoList;
        try {
            importDtoList = ExcelUtils.importExcel(file, 1, 0, NormalOutImportByColDto.class);
        } catch (Exception e) {
            throw new BizException(SysConstant.Excel_Template_Error);
        }
        Set<Integer> exceptionNoSet = importDtoList.stream().filter(
                i -> Objects.isNull(i.getNo())
                     || StrUtil.isBlank(i.getWarehouseCode())
                     || StrUtil.isBlank(i.getShippingServiceCode())
                     || StrUtil.isBlank(i.getReceiver())
                     || StrUtil.isBlank(i.getCountryRegionCode())
//                     || StrUtil.isBlank(i.getCityName())
                     || StrUtil.isBlank(i.getPostCode())
                     || StrUtil.isBlank(i.getAddress1())
                     || StrUtil.isBlank(i.getSku1())
                     || Objects.isNull(i.getQuantity1())
        ).map(NormalOutImportByColDto::getNo).collect(Collectors.toSet());
        int oldSize = importDtoList.size();
        res.setTotal(importDtoList.size());
        importDtoList = importDtoList.stream().filter(i -> !exceptionNoSet.contains(i.getNo())).collect(Collectors.toList());
        int newSize = importDtoList.size();
        if (oldSize != newSize) {
            HashMap<String, Object> map = new HashMap<>(4);
            map.put("code", StrUtil.join(",", exceptionNoSet));
            map.put("errorMsg", StringsUtil.createI18Message(SysConstant.Import_Required_Field_Blank));
            res.setFailed(res.getFailed() + oldSize - newSize);
            res.getFailedList().add(map);
        }
        if (importDtoList.isEmpty()) {
            return res;
        }
        Map<String, WarehouseDo> whCodeMatch = warehouseService.lambdaQuery()
                .eq(WarehouseDo::getTenantId, tenantId)
                .list().stream().collect(Collectors.toMap(WarehouseDo::getWhCode, val -> val));
        Map<String, ProductDo> companyIdSkuMatch = productService.lambdaQuery()
                .eq(ProductDo::getTenantId, tenantId)
                .list().stream().collect(Collectors.toMap(key -> key.getCompanyId() + key.getSku(), val -> val));
        Map<String, LogisticsChannelDo> channelCodeMatch = logisticsChannelService.lambdaQuery().eq(LogisticsChannelDo::getTenantId, tenantId).list().stream().collect(Collectors.toMap(LogisticsChannelDo::getChannelCode, val -> val, (k1, k2) -> k1));
        List<Map<String, String>> carrierList = StaticDict.Lc_Carrier.getCarrierList();
        for (NormalOutImportByColDto dto : importDtoList) {
            if (whCodeMatch.get(dto.getWarehouseCode()) == null) {
                HashMap<String, Object> map = new HashMap<>(4);
                map.put("code", dto.getNo());
                map.put("errorMsg", "Warehouse Code/仓库代码[" + dto.getWarehouseCode() + "] Wrong,Please check!");
                res.setFailed(res.getFailed() + 1);
                res.getFailedList().add(map);
                continue;
            }
            if (StrUtil.isNotBlank(dto.getSalesPlatform()) && SalesPlatformEnum.getValByName(dto.getSalesPlatform()) == null) {
                HashMap<String, Object> map = new HashMap<>(4);
                map.put("code", dto.getNo());
                map.put("errorMsg", "Sales Platform/销售平台[" + dto.getSalesPlatform() + "] Wrong,Please check!");
                res.setFailed(res.getFailed() + 1);
                res.getFailedList().add(map);
                continue;
            }
            LogisticsChannelDo logisticsChannelDo = channelCodeMatch.get(dto.getShippingServiceCode());
            if (logisticsChannelDo == null) {
                HashMap<String, Object> map = new HashMap<>(4);
                map.put("code", dto.getNo());
                map.put("errorMsg", "Shipping Service Code/物流渠道代码[" + dto.getShippingServiceCode() + "] Wrong,Please check!");
                res.setFailed(res.getFailed() + 1);
                res.getFailedList().add(map);
                continue;
            }
            if (StrUtil.isNotBlank(dto.getCarrier()) && carrierList.stream().noneMatch(i -> i.get("code").equals(dto.getCarrier()))) {
                HashMap<String, Object> map = new HashMap<>(4);
                map.put("code", dto.getNo());
                map.put("errorMsg", "Carrier/承运商[" + dto.getCarrier() + "] Wrong,Please check!");
                res.setFailed(res.getFailed() + 1);
                res.getFailedList().add(map);
                continue;
            }
            if (StrUtil.isNotBlank(dto.getSignatureService()) && OutboundSignServiceEnum.getCodeByName(dto.getSignatureService()) == null) {
                HashMap<String, Object> map = new HashMap<>(4);
                map.put("code", dto.getNo());
                map.put("errorMsg", "Signature Type/签名服务类型[" + dto.getSignatureService() + "] Wrong,Please check!");
                res.setFailed(res.getFailed() + 1);
                res.getFailedList().add(map);
                continue;
            }
            if (!CountryEnum.getCountryList().contains(dto.getCountryRegionCode())) {
                HashMap<String, Object> map = new HashMap<>(4);
                map.put("code", dto.getNo());
                map.put("errorMsg", "Recipient Country/国家/地区简码[" + dto.getCountryRegionCode() + "] Wrong,Please check!");
                res.setFailed(res.getFailed() + 1);
                res.getFailedList().add(map);
                continue;
            }
            StringBuilder skuDetailBuilder = new StringBuilder();
            List<OrderOutboundProductDo> outboundProductDoList = new ArrayList<>();
            if (companyIdSkuMatch.get(companyId + dto.getSku1()) == null) {
                HashMap<String, Object> map = new HashMap<>(4);
                map.put("code", dto.getNo());
                map.put("errorMsg", "SKU1/SKU1[" + dto.getSku1() + "] Wrong,Please check!");
                res.setFailed(res.getFailed() + 1);
                res.getFailedList().add(map);
                continue;
            } else {
                if (Objects.isNull(dto.getQuantity1()) || dto.getQuantity1() == 0) {
                    HashMap<String, Object> map = new HashMap<>(4);
                    map.put("code", dto.getNo());
                    map.put("errorMsg", "Quantity1/出库数量1[" + dto.getQuantity1() + "]It cannot be empty or equal to 0, please check");
                    res.setFailed(res.getFailed() + 1);
                    res.getFailedList().add(map);
                    continue;
                } else {
                    OrderOutboundProductDo outboundProductDo = new OrderOutboundProductDo();
                    outboundProductDo.setProductId(companyIdSkuMatch.get(companyId + dto.getSku1()).getId());
                    outboundProductDo.setForecastQty(dto.getQuantity1());
                    outboundProductDoList.add(outboundProductDo);
                    skuDetailBuilder.append(dto.getSku1()).append("*").append(dto.getQuantity1());
                }
            }
            if (StrUtil.isNotBlank(dto.getSku2()) && companyIdSkuMatch.get(companyId + dto.getSku2()) == null) {
                HashMap<String, Object> map = new HashMap<>(4);
                map.put("code", dto.getNo());
                map.put("errorMsg", "SKU2/SKU2[" + dto.getSku2() + "] Wrong,Please check!");
                res.setFailed(res.getFailed() + 1);
                res.getFailedList().add(map);
                continue;
            } else if (StrUtil.isNotBlank(dto.getSku2())) {
                if (Objects.isNull(dto.getQuantity2()) || dto.getQuantity2() == 0) {
                    HashMap<String, Object> map = new HashMap<>(4);
                    map.put("code", dto.getNo());
                    map.put("errorMsg", "Quantity2/出库数量2[" + dto.getQuantity2() + "]It cannot be empty or equal to 0, please check");
                    res.setFailed(res.getFailed() + 1);
                    res.getFailedList().add(map);
                    continue;
                } else {
                    OrderOutboundProductDo outboundProductDo = new OrderOutboundProductDo();
                    outboundProductDo.setProductId(companyIdSkuMatch.get(companyId + dto.getSku2()).getId());
                    outboundProductDo.setForecastQty(dto.getQuantity2());
                    outboundProductDoList.add(outboundProductDo);
                    skuDetailBuilder.append(",").append(dto.getSku2()).append("*").append(dto.getQuantity2());
                }
            }
            if (StrUtil.isNotBlank(dto.getSku3()) && companyIdSkuMatch.get(companyId + dto.getSku3()) == null) {
                HashMap<String, Object> map = new HashMap<>(4);
                map.put("code", dto.getNo());
                map.put("errorMsg", "SKU3/SKU3[" + dto.getSku3() + "] Wrong,Please check!");
                res.setFailed(res.getFailed() + 1);
                res.getFailedList().add(map);
                continue;
            } else if (StrUtil.isNotBlank(dto.getSku3())) {
                if (Objects.isNull(dto.getQuantity3()) || dto.getQuantity3() == 0) {
                    HashMap<String, Object> map = new HashMap<>(4);
                    map.put("code", dto.getNo());
                    map.put("errorMsg", "Quantity3/出库数量3[" + dto.getQuantity3() + "]It cannot be empty or equal to 0, please check");
                    res.setFailed(res.getFailed() + 1);
                    res.getFailedList().add(map);
                    continue;
                } else {
                    OrderOutboundProductDo outboundProductDo = new OrderOutboundProductDo();
                    outboundProductDo.setProductId(companyIdSkuMatch.get(companyId + dto.getSku3()).getId());
                    outboundProductDo.setForecastQty(dto.getQuantity3());
                    outboundProductDoList.add(outboundProductDo);
                    skuDetailBuilder.append(",").append(dto.getSku3()).append("*").append(dto.getQuantity3());
                }
            }
            if (StrUtil.isNotBlank(dto.getSku4()) && companyIdSkuMatch.get(companyId + dto.getSku4()) == null) {
                HashMap<String, Object> map = new HashMap<>(4);
                map.put("code", dto.getNo());
                map.put("errorMsg", "SKU4/SKU4[" + dto.getSku4() + "] Wrong,Please check!");
                res.setFailed(res.getFailed() + 1);
                res.getFailedList().add(map);
                continue;
            } else if (StrUtil.isNotBlank(dto.getSku4())) {
                if (Objects.isNull(dto.getQuantity4()) || dto.getQuantity4() == 0) {
                    HashMap<String, Object> map = new HashMap<>(4);
                    map.put("code", dto.getNo());
                    map.put("errorMsg", "Quantity4/出库数量4[" + dto.getQuantity4() + "]It cannot be empty or equal to 0, please check");
                    res.setFailed(res.getFailed() + 1);
                    res.getFailedList().add(map);
                    continue;
                } else {
                    OrderOutboundProductDo outboundProductDo = new OrderOutboundProductDo();
                    outboundProductDo.setProductId(companyIdSkuMatch.get(companyId + dto.getSku4()).getId());
                    outboundProductDo.setForecastQty(dto.getQuantity4());
                    outboundProductDoList.add(outboundProductDo);
                    skuDetailBuilder.append(",").append(dto.getSku4()).append("*").append(dto.getQuantity4());
                }
            }
            if (StrUtil.isNotBlank(dto.getSku5()) && companyIdSkuMatch.get(companyId + dto.getSku4()) == null) {
                HashMap<String, Object> map = new HashMap<>(4);
                map.put("code", dto.getNo());
                map.put("errorMsg", "SKU5/SKU5[" + dto.getSku5() + "] Wrong,Please check!");
                res.setFailed(res.getFailed() + 1);
                res.getFailedList().add(map);
                continue;
            } else if (StrUtil.isNotBlank(dto.getSku5())) {
                if (Objects.isNull(dto.getQuantity5()) || dto.getQuantity5() == 0) {
                    HashMap<String, Object> map = new HashMap<>(4);
                    map.put("code", dto.getNo());
                    map.put("errorMsg", "Quantity5/出库数量5[" + dto.getQuantity5() + "]It cannot be empty or equal to 0, please check");
                    res.setFailed(res.getFailed() + 1);
                    res.getFailedList().add(map);
                    continue;
                } else {
                    OrderOutboundProductDo outboundProductDo = new OrderOutboundProductDo();
                    outboundProductDo.setProductId(companyIdSkuMatch.get(companyId + dto.getSku5()).getId());
                    outboundProductDo.setForecastQty(dto.getQuantity5());
                    outboundProductDoList.add(outboundProductDo);
                    skuDetailBuilder.append(",").append(dto.getSku5()).append("*").append(dto.getQuantity5());
                }
            }
            if (StrUtil.isNotBlank(dto.getSku6()) && companyIdSkuMatch.get(companyId + dto.getSku6()) == null) {
                HashMap<String, Object> map = new HashMap<>(4);
                map.put("code", dto.getNo());
                map.put("errorMsg", "SKU6/SKU6[" + dto.getSku6() + "] Wrong,Please check!");
                res.setFailed(res.getFailed() + 1);
                res.getFailedList().add(map);
                continue;
            } else if (StrUtil.isNotBlank(dto.getSku6())) {
                if (Objects.isNull(dto.getQuantity6()) || dto.getQuantity6() == 0) {
                    HashMap<String, Object> map = new HashMap<>(4);
                    map.put("code", dto.getNo());
                    map.put("errorMsg", "Quantity6/出库数量6[" + dto.getQuantity6() + "]It cannot be empty or equal to 0, please check");
                    res.setFailed(res.getFailed() + 1);
                    res.getFailedList().add(map);
                    continue;
                } else {
                    OrderOutboundProductDo outboundProductDo = new OrderOutboundProductDo();
                    outboundProductDo.setProductId(companyIdSkuMatch.get(companyId + dto.getSku6()).getId());
                    outboundProductDo.setForecastQty(dto.getQuantity6());
                    outboundProductDoList.add(outboundProductDo);
                    skuDetailBuilder.append(",").append(dto.getSku6()).append("*").append(dto.getQuantity6());
                }
            }
            if (StrUtil.isNotBlank(dto.getSku7()) && companyIdSkuMatch.get(companyId + dto.getSku7()) == null) {
                HashMap<String, Object> map = new HashMap<>(4);
                map.put("code", dto.getNo());
                map.put("errorMsg", "SKU7/SKU7[" + dto.getSku7() + "] Wrong,Please check!");
                res.setFailed(res.getFailed() + 1);
                res.getFailedList().add(map);
                continue;
            } else if (StrUtil.isNotBlank(dto.getSku7())) {
                if (Objects.isNull(dto.getQuantity7()) || dto.getQuantity7() == 0) {
                    HashMap<String, Object> map = new HashMap<>(4);
                    map.put("code", dto.getNo());
                    map.put("errorMsg", "Quantity7/出库数量7[" + dto.getQuantity7() + "]It cannot be empty or equal to 0, please check");
                    res.setFailed(res.getFailed() + 1);
                    res.getFailedList().add(map);
                    continue;
                } else {
                    OrderOutboundProductDo outboundProductDo = new OrderOutboundProductDo();
                    outboundProductDo.setProductId(companyIdSkuMatch.get(companyId + dto.getSku7()).getId());
                    outboundProductDo.setForecastQty(dto.getQuantity7());
                    outboundProductDoList.add(outboundProductDo);
                    skuDetailBuilder.append(",").append(dto.getSku7()).append("*").append(dto.getQuantity7());
                }
            }
            if (StrUtil.isNotBlank(dto.getSku8()) && companyIdSkuMatch.get(companyId + dto.getSku8()) == null) {
                HashMap<String, Object> map = new HashMap<>(4);
                map.put("code", dto.getNo());
                map.put("errorMsg", "SKU8/SKU8[" + dto.getSku8() + "] Wrong,Please check!");
                res.setFailed(res.getFailed() + 1);
                res.getFailedList().add(map);
                continue;
            } else if (StrUtil.isNotBlank(dto.getSku8())) {
                if (Objects.isNull(dto.getQuantity8()) || dto.getQuantity8() == 0) {
                    HashMap<String, Object> map = new HashMap<>(4);
                    map.put("code", dto.getNo());
                    map.put("errorMsg", "Quantity8/出库数量8[" + dto.getQuantity8() + "]It cannot be empty or equal to 0, please check");
                    res.setFailed(res.getFailed() + 1);
                    res.getFailedList().add(map);
                    continue;
                } else {
                    OrderOutboundProductDo outboundProductDo = new OrderOutboundProductDo();
                    outboundProductDo.setProductId(companyIdSkuMatch.get(companyId + dto.getSku8()).getId());
                    outboundProductDo.setForecastQty(dto.getQuantity8());
                    outboundProductDoList.add(outboundProductDo);
                    skuDetailBuilder.append(",").append(dto.getSku8()).append("*").append(dto.getQuantity8());
                }
            }
            if (StrUtil.isNotBlank(dto.getSku9()) && companyIdSkuMatch.get(companyId + dto.getSku9()) == null) {
                HashMap<String, Object> map = new HashMap<>(4);
                map.put("code", dto.getNo());
                map.put("errorMsg", "SKU9/SKU9[" + dto.getSku9() + "] Wrong,Please check!");
                res.setFailed(res.getFailed() + 1);
                res.getFailedList().add(map);
                continue;
            } else if (StrUtil.isNotBlank(dto.getSku9())) {
                if (Objects.isNull(dto.getQuantity9()) || dto.getQuantity9() == 0) {
                    HashMap<String, Object> map = new HashMap<>(4);
                    map.put("code", dto.getNo());
                    map.put("errorMsg", "Quantity9/出库数量9[" + dto.getQuantity9() + "]It cannot be empty or equal to 0, please check");
                    res.setFailed(res.getFailed() + 1);
                    res.getFailedList().add(map);
                    continue;
                } else {
                    OrderOutboundProductDo outboundProductDo = new OrderOutboundProductDo();
                    outboundProductDo.setProductId(companyIdSkuMatch.get(companyId + dto.getSku9()).getId());
                    outboundProductDo.setForecastQty(dto.getQuantity9());
                    outboundProductDoList.add(outboundProductDo);
                    skuDetailBuilder.append(",").append(dto.getSku9()).append("*").append(dto.getQuantity9());
                }
            }
            if (StrUtil.isNotBlank(dto.getSku10()) && companyIdSkuMatch.get(companyId + dto.getSku10()) == null) {
                HashMap<String, Object> map = new HashMap<>(4);
                map.put("code", dto.getNo());
                map.put("errorMsg", "SKU10/SKU10[" + dto.getSku10() + "] Wrong,Please check!");
                res.setFailed(res.getFailed() + 1);
                res.getFailedList().add(map);
                continue;
            } else if (StrUtil.isNotBlank(dto.getSku10())) {
                if (Objects.isNull(dto.getQuantity10()) || dto.getQuantity10() == 0) {
                    HashMap<String, Object> map = new HashMap<>(4);
                    map.put("code", dto.getNo());
                    map.put("errorMsg", "Quantity10/出库数量10[" + dto.getQuantity10() + "]It cannot be empty or equal to 0, please check");
                    res.setFailed(res.getFailed() + 1);
                    res.getFailedList().add(map);
                    continue;
                } else {
                    OrderOutboundProductDo outboundProductDo = new OrderOutboundProductDo();
                    outboundProductDo.setProductId(companyIdSkuMatch.get(companyId + dto.getSku10()).getId());
                    outboundProductDo.setForecastQty(dto.getQuantity10());
                    outboundProductDoList.add(outboundProductDo);
                    skuDetailBuilder.append(",").append(dto.getSku10()).append("*").append(dto.getQuantity10());
                }
            }
            this.importNormalOutboundSave(
                    dto,
                    null,
                    skuDetailBuilder.toString(),
                    whCodeMatch,
                    outboundProductDoList,
                    logisticsChannelDo,
                    userId,
                    username
            );
            res.setSuccess(res.getSuccess() + 1);
        }
        return res;
    }

    @Override
    public CommonBatchOperationVo importNormalOutbound2(MultipartFile file, Long tenantId, Long userId, String username, Long companyId) {
        CommonBatchOperationVo res = new CommonBatchOperationVo();
        List<NormalOutImportByRowDto> importDtoList;
        try {
            importDtoList = ExcelUtils.importExcel(file, 1, 0, NormalOutImportByRowDto.class);
        } catch (Exception e) {
            throw new BizException(SysConstant.Excel_Template_Error);
        }
        Set<Integer> exceptionIdSet = importDtoList.stream().filter(
                i -> Objects.isNull(i.getIdentificationNo())
                     || StrUtil.isBlank(i.getWarehouseCode())
                     || StrUtil.isBlank(i.getShippingServiceCode())
                     || StrUtil.isBlank(i.getReceiver())
                     || StrUtil.isBlank(i.getCountryRegionCode())
//                     || StrUtil.isBlank(i.getCityName())
                     || StrUtil.isBlank(i.getPostCode())
                     || StrUtil.isBlank(i.getAddress1())
                     || StrUtil.isBlank(i.getSku())
                     || Objects.isNull(i.getQuantity())
        ).map(NormalOutImportByRowDto::getIdentificationNo).collect(Collectors.toSet());
        int oldSize = importDtoList.size();
        res.setTotal(importDtoList.size());
        importDtoList = importDtoList.stream().filter(i -> !exceptionIdSet.contains(i.getIdentificationNo())).collect(Collectors.toList());
        int newSize = importDtoList.size();
        if (oldSize != newSize) {
            HashMap<String, Object> map = new HashMap<>(4);
            map.put("code", StrUtil.join(",", exceptionIdSet));
            map.put("errorMsg", StringsUtil.createI18Message(SysConstant.Import_Required_Field_Blank));
            res.setFailed(res.getFailed() + oldSize - newSize);
            res.getFailedList().add(map);
        }
        if (importDtoList.isEmpty()) {
            return res;
        }
        Map<String, WarehouseDo> whCodeMatch = warehouseService.lambdaQuery()
                .eq(WarehouseDo::getTenantId, tenantId)
                .list().stream().collect(Collectors.toMap(WarehouseDo::getWhCode, val -> val));
        Map<String, ProductDo> companyIdSkuMatch = productService.lambdaQuery()
                .eq(ProductDo::getTenantId, tenantId)
                .list().stream().collect(Collectors.toMap(key -> key.getCompanyId() + key.getSku(), val -> val));
        Map<String, LogisticsChannelDo> channelCodeMatch = logisticsChannelService.lambdaQuery().eq(LogisticsChannelDo::getTenantId, tenantId).list().stream().collect(Collectors.toMap(LogisticsChannelDo::getChannelCode, val -> val, (v1, v2) -> v1));
        List<Map<String, String>> carrierList = StaticDict.Lc_Carrier.getCarrierList();
        //数据根据id分组
        Map<Integer, List<NormalOutImportByRowDto>> dtoListByIdGroup = importDtoList.stream().collect(Collectors.groupingBy(NormalOutImportByRowDto::getIdentificationNo));
        for (Map.Entry<Integer, List<NormalOutImportByRowDto>> dtoListByIdEntry : dtoListByIdGroup.entrySet()) {
            List<NormalOutImportByRowDto> dtoList = dtoListByIdEntry.getValue();
            NormalOutImportByRowDto dto = dtoList.get(0);
            if (whCodeMatch.get(dto.getWarehouseCode()) == null) {
                HashMap<String, Object> map = new HashMap<>(4);
                map.put("code", dto.getIdentificationNo());
                map.put("errorMsg", "Warehouse Code/仓库代码[" + dto.getWarehouseCode() + "] Wrong,Please check!");
                res.setFailed(res.getFailed() + dtoList.size());
                res.getFailedList().add(map);
                continue;
            }
            if (StrUtil.isNotBlank(dto.getSalesPlatform()) && SalesPlatformEnum.getValByName(dto.getSalesPlatform()) == null) {
                HashMap<String, Object> map = new HashMap<>(4);
                map.put("code", dto.getIdentificationNo());
                map.put("errorMsg", "Sales Platform/销售平台[" + dto.getSalesPlatform() + "] Wrong,Please check!");
                res.setFailed(res.getFailed() + dtoList.size());
                res.getFailedList().add(map);
                continue;
            }
            LogisticsChannelDo logisticsChannelDo = channelCodeMatch.get(dto.getShippingServiceCode());
            if (logisticsChannelDo == null) {
                HashMap<String, Object> map = new HashMap<>(4);
                map.put("code", dto.getIdentificationNo());
                map.put("errorMsg", "Shipping Service Code/物流渠道代码[" + dto.getShippingServiceCode() + "] Wrong,Please check!");
                res.setFailed(res.getFailed() + dtoList.size());
                res.getFailedList().add(map);
                continue;
            }
            if (StrUtil.isNotBlank(dto.getCarrier()) && carrierList.stream().noneMatch(i -> i.get("code").equals(dto.getCarrier()))) {
                HashMap<String, Object> map = new HashMap<>(4);
                map.put("code", dto.getIdentificationNo());
                map.put("errorMsg", "Carrier/承运商[" + dto.getCarrier() + "] Wrong,Please check!");
                res.setFailed(res.getFailed() + dtoList.size());
                res.getFailedList().add(map);
                continue;
            }
            if (StrUtil.isNotBlank(dto.getSignatureService()) && OutboundSignServiceEnum.getCodeByName(dto.getSignatureService()) == null) {
                HashMap<String, Object> map = new HashMap<>(4);
                map.put("code", dto.getIdentificationNo());
                map.put("errorMsg", "Signature Type/签名服务类型[" + dto.getSignatureService() + "] Not Found,Please check!");
                res.setFailed(res.getFailed() + dtoList.size());
                res.getFailedList().add(map);
                continue;
            }
            if (!CountryEnum.getCountryList().contains(dto.getCountryRegionCode())) {
                HashMap<String, Object> map = new HashMap<>(4);
                map.put("code", dto.getIdentificationNo());
                map.put("errorMsg", "Recipient Country/国家/地区[" + dto.getCountryRegionCode() + "] Wrong,Please check!");
                res.setFailed(res.getFailed() + dtoList.size());
                res.getFailedList().add(map);
                continue;
            }
            Optional<NormalOutImportByRowDto> notFindSku = dtoList.stream().filter(i -> companyIdSkuMatch.get(companyId + i.getSku()) == null).findFirst();
            if (notFindSku.isPresent()) {
                HashMap<String, Object> map = new HashMap<>(4);
                map.put("code", dto.getIdentificationNo());
                map.put("errorMsg", "SKU/SKU[" + notFindSku.get().getSku() + "] Not Found,Please check!");
                res.setFailed(res.getFailed() + dtoList.size());
                res.getFailedList().add(map);
                continue;
            }
            StringBuilder skuDetailBuilder = new StringBuilder();
            List<OrderOutboundProductDo> outboundProductDoList = new ArrayList<>();
            for (NormalOutImportByRowDto normalOutImportByRowDto : dtoList) {
                OrderOutboundProductDo outboundProductDo = new OrderOutboundProductDo();
                outboundProductDo.setProductId(companyIdSkuMatch.get(companyId + normalOutImportByRowDto.getSku()).getId());
                outboundProductDo.setForecastQty(normalOutImportByRowDto.getQuantity());
                outboundProductDoList.add(outboundProductDo);
                skuDetailBuilder.append(normalOutImportByRowDto.getSku()).append("*").append(normalOutImportByRowDto.getQuantity()).append(",");
            }
            this.importNormalOutboundSave(
                    null,
                    dto,
                    skuDetailBuilder.substring(0, skuDetailBuilder.length() - 1),
                    whCodeMatch,
                    outboundProductDoList,
                    logisticsChannelDo,
                    userId,
                    username
            );
            res.setSuccess(res.getSuccess() + 1);
        }
        return res;
    }

    public void importNormalOutboundSave(NormalOutImportByColDto colDto,
                                         NormalOutImportByRowDto rowDto,
                                         String skuDetail,
                                         Map<String, WarehouseDo> whCodeMatch,
                                         List<OrderOutboundProductDo> outboundProductDoList,
                                         LogisticsChannelDo logisticsChannelDo,
                                         Long userId, String username) {
        OrderOutboundDo entityDo = new OrderOutboundDo();
//        BeanUtils.copyProperties(Objects.isNull(colDto) ? rowDto : colDto, entityDo);
        if (Objects.isNull(colDto)) {
            BeanConvert.INSTANCE.normalOutImportByRowDtoToOutboundDo(rowDto, entityDo);
        } else {
            BeanConvert.INSTANCE.normalOutImportByColDtoToOutboundDo(colDto, entityDo);
        }
        entityDo.setSkuDetail(skuDetail);
        entityDo.setWhId(whCodeMatch.get(Objects.isNull(colDto) ? rowDto.getWarehouseCode() : colDto.getWarehouseCode()).getId());
        entityDo.setOutboundNo(outboundService.generateOutboundNo("OBS"));
        outboundProductDoList = new ArrayList<>(outboundProductDoList.stream().collect(Collectors.toMap(OrderOutboundProductDo::getProductId, val -> val, (o1, o2) -> {
            o1.setForecastQty(o1.getForecastQty() + o2.getForecastQty());
            return o1;
        })).values());
        if (outboundProductDoList.size() == 1) {
            Integer forecastQty = outboundProductDoList.get(0).getForecastQty();
            if (forecastQty > 1) {
                entityDo.setOrderVarietyType(OutboundVarietyTypeEnum.two.getValue());
            } else {
                entityDo.setOrderVarietyType(OutboundVarietyTypeEnum.one.getValue());
            }
        } else {
            entityDo.setOrderVarietyType(OutboundVarietyTypeEnum.three.getValue());
        }
        entityDo.setOrderType(OutboundTypeEnum.one.getValue());
        entityDo.setLogisticsChannelId(logisticsChannelDo.getId());
        if (StrUtil.isEmpty(Objects.isNull(colDto) ? rowDto.getCarrier() : colDto.getCarrier())) {
            entityDo.setCarrier(logisticsChannelDo.getCarrierCode());
        }
        entityDo.setStatus(NormalOutboundStatusEnum.zero.getValue());
        entityDo.setSalesPlatform(SalesPlatformEnum.getValByName(Objects.isNull(colDto) ? rowDto.getSalesPlatform() : colDto.getSalesPlatform()));
        entityDo.setSignatureService(OutboundSignServiceEnum.getCodeByName(Objects.isNull(colDto) ? rowDto.getSignatureService() : colDto.getSignatureService()));
        if (Objects.nonNull(Objects.isNull(colDto) ? rowDto.getInsureMoney() : colDto.getInsureMoney())) {
            entityDo.setInsuranceFlag(Boolean.TRUE);
        } else {
            entityDo.setInsuranceFlag(Boolean.FALSE);
        }
        entityDo.setTotalNum(outboundProductDoList.stream().map(OrderOutboundProductDo::getForecastQty).reduce(0, Integer::sum));
        outboundService.save(entityDo);
        //操作子表
        outboundProductDoList.forEach(orderOutboundProductDo -> {
            orderOutboundProductDo.setOutboundId(entityDo.getId());
        });
        outboundProductService.saveBatch(outboundProductDoList);
        //为计算包裹尺寸组装数据
        Map<Long, Integer> productIdMatchNum = outboundProductDoList.stream().collect(
                Collectors.toMap(
                        OrderOutboundProductDo::getProductId,
                        OrderOutboundProductDo::getForecastQty,
                        Integer::sum
                )
        );
        List<ProductDo> productDoList = productService.lambdaQuery()
                .in(ProductDo::getId, productIdMatchNum.keySet())
                .list();
        List<PackageSizeVo> packageSizeVoList = new ArrayList<>();
        productDoList.forEach(productDo ->
                packageSizeVoList.add(
                        PackageSizeVo.builder()
                                .productId(productDo.getId())
                                .sku(productDo.getSku())
                                .qty(productIdMatchNum.get(productDo.getId()))
                                .length(productDo.getWmsLength() == null ? productDo.getLength() : productDo.getWmsLength())
                                .width(productDo.getWmsWidth() == null ? productDo.getWidth() : productDo.getWmsWidth())
                                .height(productDo.getWmsHeight() == null ? productDo.getHeight() : productDo.getWmsHeight())
                                .weight(productDo.getWmsWeight() == null ? productDo.getWeight() : productDo.getWmsWeight())
                                .build()
                )
        );
        //根据产品列表计算包裹尺寸
        outboundService.calculatePackageSize(entityDo.getId(), 1, entityDo.getTrackingNumber(), entityDo.getLabelFileId(), packageSizeVoList, true);
        //新增创建出库单进度
        outboundService.createNormalProgress(entityDo, username);
        operateRecordService.operateAddRecordAndValue(
                SysConstant.Outbound_Import,
                "",
                entityDo.getId(),
                userId,
                username,
                OperateTypeEnum.OrderOutbound
        );
    }

    @Override
    public CommonBatchOperationVo importNormalOutboundTrackingNumber(MultipartFile file, Long tenantId, Long userId, String username, Long companyId) {
        CommonBatchOperationVo res = new CommonBatchOperationVo();
        List<NormalOutImportTrackingNoOmsDto> importDtoList;
        try {
            importDtoList = ExcelUtils.importExcel(file, 1, 0, NormalOutImportTrackingNoOmsDto.class);
        } catch (Exception e) {
            throw new BizException(SysConstant.Excel_Template_Error);
        }
        List<String> exceptionOutboundNoList = importDtoList.stream().filter(
                i -> StrUtil.isBlank(i.getOutboundNo())
                     || StrUtil.isBlank(i.getTrackingNumber())
        ).map(NormalOutImportTrackingNoOmsDto::getOutboundNo).collect(Collectors.toList());
        int oldSize = importDtoList.size();
        res.setTotal(importDtoList.size());
        importDtoList = importDtoList.stream()
                .filter(i -> !exceptionOutboundNoList.contains(i.getOutboundNo()))
                .collect(Collectors.toList());
        int newSize = importDtoList.size();
        if (oldSize != newSize) {
            HashMap<String, Object> map = new HashMap<>(4);
            map.put("code", StrUtil.join(",", exceptionOutboundNoList));
            map.put("errorMsg", StringsUtil.createI18Message(SysConstant.Import_Required_Field_Blank));
            res.setFailed(res.getFailed() + oldSize - newSize);
            res.getFailedList().add(map);
        }
        if (importDtoList.isEmpty()) {
            return res;
        }
        Map<String, OrderOutboundDo> outboundNoMatch = outboundService.lambdaQuery()
                .in(OrderOutboundDo::getOutboundNo, importDtoList.stream().map(NormalOutImportTrackingNoOmsDto::getOutboundNo).collect(Collectors.toList()))
                .eq(Objects.nonNull(companyId), OrderOutboundDo::getCompanyId, companyId)
                .ge(OrderOutboundDo::getCreateTime, DateUtil.offset(DateUtil.date(), DateField.MONTH, -3))
                .list().stream().collect(Collectors.toMap(OrderOutboundDo::getOutboundNo, val -> val));
        for (NormalOutImportTrackingNoOmsDto dto : importDtoList) {
            OrderOutboundDo outboundDo = outboundNoMatch.get(dto.getOutboundNo());
            if (outboundDo == null) {
                HashMap<String, Object> map = new HashMap<>(4);
                map.put("code", dto.getOutboundNo());
                map.put("errorMsg", StringsUtil.createI18Message(SysConstant.Outbound_Order_Does_Not_Exist));
                res.setFailed(res.getFailed() + 1);
                res.getFailedList().add(map);
                continue;
            }
            outboundDo.setTrackingNumber(dto.getTrackingNumber());
            outboundService.updateById(outboundDo);
            operateRecordService.operateAddRecordAndValue(
                    SysConstant.Outbound_Import_Tracking_Number,
                    "",
                    outboundDo.getId(),
                    userId,
                    username,
                    OperateTypeEnum.OrderOutbound
            );
            res.setSuccess(res.getSuccess() + 1);
        }
        return res;
    }

    @Override
    public CommonBatchOperationVo importNormalOutboundExpress(ImportNormalOutboundExpressBo bo, Long tenantId, Long userId, String username) {
        CommonBatchOperationVo res = new CommonBatchOperationVo();
        if (CollUtil.isEmpty(bo.getDataMap())) {
            return res;
        }
        res.setTotal(bo.getDataMap().size());
        SFunction<OrderOutboundDo, String> func = bo.getType().equals(1) ? OrderOutboundDo::getOutboundNo : OrderOutboundDo::getTrackingNumber;
        Map<String, OrderOutboundDo> keyMatchDo = outboundService.lambdaQuery()
                .in(func, bo.getDataMap().keySet())
                .ge(OrderOutboundDo::getCreateTime, DateUtil.offset(DateUtil.date(), DateField.MONTH, -3))
                .list().stream().collect(Collectors.toMap(func, val -> val));
        for (Map.Entry<String, Long> entry : bo.getDataMap().entrySet()) {
            OrderOutboundDo outboundDo = keyMatchDo.get(entry.getKey());
            if (outboundDo == null) {
                HashMap<String, Object> map = new HashMap<>(4);
                map.put("code", entry.getKey());
                map.put("errorMsg", bo.getType().equals(1) ? StringsUtil.createI18Message(SysConstant.Outbound_Order_Does_Not_Exist) : StringsUtil.createI18Message(SysConstant.TrackingNumber_Not_Found));
                res.setFailed(res.getFailed() + 1);
                res.getFailedList().add(map);
                continue;
            }
            outboundDo.setLabelFileId(entry.getValue());
            outboundService.updateById(outboundDo);
            operateRecordService.operateAddRecordAndValue(
                    SysConstant.Outbound_Import_Express,
                    "",
                    outboundDo.getId(),
                    userId,
                    username,
                    OperateTypeEnum.OrderOutbound
            );
            //上传到正式目录
            fileService.uploadFile(entry.getValue());
            res.setSuccess(res.getSuccess() + 1);
        }
        return res;
    }

    @Override
    public void getWholesaleOutboundImportTemplate1(HttpServletResponse response, Long tenantId, Long companyId) throws IOException {
        TemplateExportParams params = new TemplateExportParams(
                "excelTemplates/WholesaleOutbound_Import_col.xlsx", true);
        response.setCharacterEncoding("UTF-8");
        response.setHeader("content-Type", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
        response.setHeader("Access-Control-Expose-Headers", "Content-Disposition");
        response.setHeader("Content-Disposition", "attachment;filename=WholesaleOutbound_Import_1.xlsx");
        //导出数据
        try (Workbook book = ExcelExportUtil.exportExcel(params, this.buildOutboundTemplateSheetMap(tenantId, companyId))) {
            book.write(response.getOutputStream());
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            response.getOutputStream().flush();
            response.getOutputStream().close();
        }
    }

    @Override
    public void getWholesaleOutboundImportTemplate2(HttpServletResponse response, Long tenantId, Long companyId) throws IOException {
        TemplateExportParams params = new TemplateExportParams(
                "excelTemplates/WholesaleOutbound_Import_row.xlsx", true);
        response.setCharacterEncoding("UTF-8");
        response.setHeader("content-Type", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
        response.setHeader("Access-Control-Expose-Headers", "Content-Disposition");
        response.setHeader("Content-Disposition", "attachment;filename=WholesaleOutbound_Import_2.xlsx");
        //导出数据
        try (Workbook book = ExcelExportUtil.exportExcel(params, this.buildOutboundTemplateSheetMap(tenantId, companyId))) {
            book.write(response.getOutputStream());
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            response.getOutputStream().flush();
            response.getOutputStream().close();
        }
    }

    @Override
    public void getFbaAllotOutboundImportTemplate(HttpServletResponse response, Long tenantId, Long companyId) throws IOException {
        TemplateExportParams params = new TemplateExportParams(
                "excelTemplates/AllotOutbound_Import_Fba.xlsx", true);
        response.setCharacterEncoding("UTF-8");
        response.setHeader("content-Type", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
        response.setHeader("Access-Control-Expose-Headers", "Content-Disposition");
        response.setHeader("Content-Disposition", "attachment;filename=AllotOutbound_Import_Fba.xlsx");
        //导出数据
        try (Workbook book = ExcelExportUtil.exportExcel(params, this.buildFbaAllocateTemplateSheetMap(tenantId, companyId))) {
            book.write(response.getOutputStream());
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            response.getOutputStream().flush();
            response.getOutputStream().close();
        }
    }

    @Override
    public void get3rdWhAllotOutboundImportTemplate(HttpServletResponse response, Long tenantId, Long companyId) throws IOException {
        TemplateExportParams params = new TemplateExportParams(
                "excelTemplates/AllotOutbound_Import_3rdWh.xlsx", true);
        response.setCharacterEncoding("UTF-8");
        response.setHeader("content-Type", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
        response.setHeader("Access-Control-Expose-Headers", "Content-Disposition");
        response.setHeader("Content-Disposition", "attachment;filename=AllotOutbound_Import_3rdWh.xlsx");
        //导出数据
        try (Workbook book = ExcelExportUtil.exportExcel(params, this.build3rdWhAllocateTemplateSheetMap(tenantId, companyId))) {
            book.write(response.getOutputStream());
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            response.getOutputStream().flush();
            response.getOutputStream().close();
        }
    }

    @Override
    public void normalOutboundExportOms(NormalOrderOutboundQueryBo bo, Long companyId, HttpServletResponse response) throws IOException {
        List<NormalOutboundVo> list = outboundDao.getNormalListPage(bo);
        if (CollUtil.isEmpty(list)) {
            throw new BizException(SysConstant.No_Data);
        }
        //出库单id
        List<Long> orderIdList = list.stream().map(NormalOutboundVo::getId).collect(Collectors.toList());
        Map<Long, WarehouseDo> whIdMatch = warehouseService.lambdaQuery()
                .in(WarehouseDo::getId, list.stream().map(NormalOutboundVo::getWhId).collect(Collectors.toList()))
                .list().stream().collect(Collectors.toMap(WarehouseDo::getId, val -> val));
        Map<Long, LogisticsChannelDo> lcIdMatch = logisticsChannelService.lambdaQuery()
                .in(LogisticsChannelDo::getId, list.stream().map(NormalOutboundVo::getLogisticsChannelId).collect(Collectors.toList())).list().stream().collect(Collectors.toMap(LogisticsChannelDo::getId, val -> val));
        Map<Long, OrderOutboundPackageDo> outboundIdMatchPack = outboundPackageService.lambdaQuery()
                .in(OrderOutboundPackageDo::getOutboundId, orderIdList).list().stream().collect(Collectors.toMap(OrderOutboundPackageDo::getOutboundId, val -> val, (o1, o2) -> o1));
        Map<Long, List<OrderOutboundPackageMaterialDo>> packIdMatchMaterial = outboundPackageMaterialService.lambdaQuery()
                .in(OrderOutboundPackageMaterialDo::getPackageId, outboundIdMatchPack.values().stream().map(OrderOutboundPackageDo::getId).collect(Collectors.toList()))
                .list().stream().collect(Collectors.groupingBy(OrderOutboundPackageMaterialDo::getPackageId));
        Map<Long, List<OrderOutboundProductDo>> outboundIdMatchProductList = outboundProductService.lambdaQuery()
                .in(OrderOutboundProductDo::getOutboundId, orderIdList)
                .list().stream().collect(Collectors.groupingBy(OrderOutboundProductDo::getOutboundId));
        Map<Long, ProductDo> productIdMatch = productService.lambdaQuery()
                .in(ProductDo::getId, outboundIdMatchProductList.values().stream().flatMap(List::stream).map(OrderOutboundProductDo::getProductId).collect(Collectors.toList()))
                .list().stream().collect(Collectors.toMap(ProductDo::getId, val -> val));
        List<Long> storeIdList = list.stream().map(NormalOutboundVo::getStoreId).filter(Objects::nonNull).collect(Collectors.toList());
        Map<Long, PlatformStoreDo> storeIdMatchDo = new HashMap<>();
        if (CollUtil.isNotEmpty(storeIdList)) {
            storeIdMatchDo = platformStoreService.listByIds(storeIdList).stream().collect(Collectors.toMap(PlatformStoreDo::getId, val -> val));
        }
        OmsSystemEntity omsSet = systemSetService.getOmsSet(companyId);
        List<NormalOutExportOmsDto> dtoList = new ArrayList<>(list.size());
        for (NormalOutboundVo vo : list) {
//            NormalOutExportOmsDto dto = new NormalOutExportOmsDto();
//            BeanUtils.copyProperties(vo, dto);
            NormalOutExportOmsDto dto = BeanConvert.INSTANCE.normalOutboundVoToNormalOutExportOmsDto(vo);
            WarehouseDo warehouseDo = whIdMatch.get(vo.getWhId());
            if (warehouseDo == null) {
                dto.setWarehouseNameCode("");
            } else {
                dto.setWarehouseNameCode(warehouseDo.getWhNameCn() + "(" + warehouseDo.getWhCode() + ")");
            }
            dto.setSalesPlatform(SalesPlatformEnum.getNameByVal(vo.getSalesPlatform()));
            PlatformStoreDo platformStoreDo = storeIdMatchDo.get(vo.getStoreId());
            if (platformStoreDo != null) {
                dto.setStoreName(platformStoreDo.getStoreName());
            }
            dto.setSignatureService(OutboundSignServiceEnum.getNameByVal(vo.getSignatureService()));
            dto.setStatus(NormalOutboundStatusEnum.getOmsDesc(vo.getStatus()));
            LogisticsChannelDo channelDo = lcIdMatch.get(vo.getLogisticsChannelId());
            if (Objects.nonNull(channelDo)) {
                dto.setShippingService(channelDo.getChannelCode());
                if (StrUtil.isNotBlank(vo.getCustomizeChannelName())) {
                    dto.setShippingService(dto.getShippingService() + ":" + vo.getCustomizeChannelName());
                }
            } else {
                dto.setShippingService("自动选取价格最低的物流渠道");
            }
            if (Objects.isNull(vo.getInsureMoney())) {
                dto.setInsureMoney(BigDecimal.valueOf(0.00));
            }
            //出库产品列表
            List<OrderOutboundProductDo> outboundProductDoList = outboundIdMatchProductList.get(vo.getId());
            dto.setSkuKind(Math.toIntExact(outboundProductDoList.stream().map(OrderOutboundProductDo::getProductId).distinct().count()));
            dto.setOrderOrigin(OutboundOriginEnum.getDescByValue(vo.getOrderOrigin()));
            dto.setCountry(CountryEnum.getNameEnByCountryRegionCode(vo.getCountryRegionCode()));
            OrderOutboundPackageDo packageDo = outboundIdMatchPack.get(vo.getId());
            String packMaterial = null;
            List<OrderOutboundPackageMaterialDo> packageMaterialDoList = packIdMatchMaterial.get(packageDo.getId());
            if (CollUtil.isNotEmpty(packageMaterialDoList)) {
                packMaterial = packageMaterialDoList.stream().map(item -> item.getMaterialCode() + "*" + item.getMaterialNum()).collect(Collectors.joining(","));
            }
            String packageSize = null;
            String packageWeight = null;
            if (StaticDict.OMP_UnitMeasurement.Cm.getValue().equals(omsSet.getUnitMeasurement().getUnit())) {
                packageSize = packageDo.getLength() + "*" + packageDo.getWidth() + "*" + packageDo.getHeight() + "cm";
                packageWeight = packageDo.getWeight() + "kg";
            } else if (StaticDict.OMP_UnitMeasurement.In.getValue().equals(omsSet.getUnitMeasurement().getUnit())) {
                packageSize = packageDo.getLengthBs() + "*" + packageDo.getWidthBs() + "*" + packageDo.getHeightBs() + "in";
                packageWeight = packageDo.getWeightBs() + "lb";
            }
            dto.setPackageSize(packageSize);
            dto.setPackageWeight(packageWeight);
            for (int i = 1; i <= outboundProductDoList.size(); i++) {
                OrderOutboundProductDo outboundProductDo = outboundProductDoList.get(i - 1);
                ProductDo productDo = productIdMatch.get(outboundProductDo.getProductId());
                if (productDo == null) continue;
//                NormalOutExportOmsDto dtoTemp = new NormalOutExportOmsDto();
//                BeanUtils.copyProperties(dto, dtoTemp);
                NormalOutExportOmsDto dtoTemp = BeanConvert.INSTANCE.copyNormalOutExportOmsDto(dto);
                dtoTemp.setSku(productDo.getSku());
                dtoTemp.setItf(productDo.getItf());
                dtoTemp.setJanCode(productDo.getJanCode());
                dtoTemp.setProductName(productDo.getProductName());
                dtoTemp.setOutboundQty(outboundProductDo.getForecastQty());
                if (StaticDict.OMP_UnitMeasurement.Cm.getValue().equals(omsSet.getUnitMeasurement().getUnit())) {
                    dtoTemp.setLength(productDo.getLength());
                    dtoTemp.setWidth(productDo.getWidth());
                    dtoTemp.setHeight(productDo.getHeight());
                    dtoTemp.setWeight(productDo.getWeight());
                    dtoTemp.setWmsLength(productDo.getWmsLength());
                    dtoTemp.setWmsWidth(productDo.getWmsWidth());
                    dtoTemp.setWmsHeight(productDo.getWmsHeight());
                    dtoTemp.setWmsWeight(productDo.getWmsWeight());
                    dtoTemp.setSizeUnit("cm");
                    dtoTemp.setWeightUnit("kg");
                } else if (StaticDict.OMP_UnitMeasurement.In.getValue().equals(omsSet.getUnitMeasurement().getUnit())) {
                    dtoTemp.setLength(productDo.getLengthBs());
                    dtoTemp.setWidth(productDo.getWidthBs());
                    dtoTemp.setHeight(productDo.getHeightBs());
                    dtoTemp.setWeight(productDo.getWeightBs());
                    dtoTemp.setWmsLength(productDo.getWmsLengthBs());
                    dtoTemp.setWmsWidth(productDo.getWmsWidthBs());
                    dtoTemp.setWmsHeight(productDo.getWmsHeightBs());
                    dtoTemp.setWmsWeight(productDo.getWmsWeightBs());
                    dtoTemp.setSizeUnit("in");
                    dtoTemp.setWeightUnit("lb");
                }
                if (i == 1) {
                    dtoTemp.setPackageMaterialCodeQty(packMaterial);
                }
                dtoList.add(dtoTemp);
            }
        }
        ExportParams exportParams = new ExportParams();
        //设置样式
        exportParams.setStyle(ExcelExportTitleStyle.class);
        // 设置sheet的名称
        exportParams.setSheetName(bo.getSource() + "一件代发出库单导出");
        ExcelUtils.exportExcel(dtoList, NormalOutExportOmsDto.class, "NormalOutbound_Export_" + DateUtil.format(DateUtil.date(), "yyyyMMddHHmmss"), exportParams, response, "xls");
    }

    @Override
    public void normalOutboundExportBySimple(NormalOrderOutboundQueryBo bo, HttpServletResponse response) throws IOException {
        List<NormalOutboundVo> list = outboundDao.getNormalListPage(bo);
        if (CollUtil.isEmpty(list)) {
            throw new BizException(SysConstant.No_Data);
        }
        Map<Long, CompanyDo> companyIdMatch = companyService.lambdaQuery()
                .in(CompanyDo::getId, list.stream().map(NormalOutboundVo::getCompanyId).collect(Collectors.toSet()))
                .list().stream().collect(Collectors.toMap(CompanyDo::getId, val -> val));
        Map<Long, WarehouseDo> whIdMatch = warehouseService.lambdaQuery()
                .in(WarehouseDo::getId, list.stream().map(NormalOutboundVo::getWhId).collect(Collectors.toList()))
                .list().stream().collect(Collectors.toMap(WarehouseDo::getId, val -> val));
        Map<Long, LogisticsChannelDo> lcIdMatch = logisticsChannelService.lambdaQuery()
                .in(LogisticsChannelDo::getId, list.stream().map(NormalOutboundVo::getLogisticsChannelId).collect(Collectors.toList())).list().stream().collect(Collectors.toMap(LogisticsChannelDo::getId, val -> val));
        Map<Long, List<OrderOutboundProductDo>> outIdMatchProductList = outboundProductService.lambdaQuery()
                .in(OrderOutboundProductDo::getOutboundId, list.stream().map(NormalOutboundVo::getId).collect(Collectors.toList())).list().stream().collect(Collectors.groupingBy(OrderOutboundProductDo::getOutboundId));
        Map<Long, ProductDo> productIdMatch = productService.lambdaQuery()
                .in(ProductDo::getId, outIdMatchProductList.values().stream().flatMap(List::stream).map(OrderOutboundProductDo::getProductId).collect(Collectors.toList()))
                .list().stream().collect(Collectors.toMap(ProductDo::getId, val -> val));
        List<Long> storeIdList = list.stream().map(NormalOutboundVo::getStoreId).filter(Objects::nonNull).collect(Collectors.toList());
        Map<Long, PlatformStoreDo> storeIdMatchDo = new HashMap<>();
        if (CollUtil.isNotEmpty(storeIdList)) {
            storeIdMatchDo = platformStoreService.listByIds(storeIdList).stream().collect(Collectors.toMap(PlatformStoreDo::getId, val -> val));
        }
        List<NormalOutExportBySimpleDto> dtoList = new ArrayList<>(list.size());
        for (NormalOutboundVo vo : list) {
//            NormalOutExportBySimpleDto dto = new NormalOutExportBySimpleDto();
//            BeanUtils.copyProperties(vo, dto);
            NormalOutExportBySimpleDto dto = BeanConvert.INSTANCE.normalOutboundVoToNormalOutExportBySimpleDto(vo);
            CompanyDo companyDo = companyIdMatch.get(vo.getCompanyId());
            if (companyDo == null) {
                dto.setCompanyNameCode("");
            } else {
                dto.setCompanyNameCode(companyDo.getName() + "(" + companyDo.getCompanyCode() + ")");
            }
            WarehouseDo warehouseDo = whIdMatch.get(vo.getWhId());
            if (warehouseDo == null) {
                dto.setWarehouseNameCode("");
            } else {
                dto.setWarehouseNameCode(warehouseDo.getWhNameCn() + "(" + warehouseDo.getWhCode() + ")");
            }
            dto.setSalesPlatform(SalesPlatformEnum.getNameByVal(vo.getSalesPlatform()));
            PlatformStoreDo platformStoreDo = storeIdMatchDo.get(vo.getStoreId());
            if (platformStoreDo != null) {
                dto.setStoreName(platformStoreDo.getStoreName());
            }
            if ("oms".equals(bo.getSource())) {
                dto.setStatus(NormalOutboundStatusEnum.getOmsDesc(vo.getStatus()));
            } else {
                dto.setStatus(NormalOutboundStatusEnum.getDescByValue(vo.getStatus()));
            }
            LogisticsChannelDo channelDo = lcIdMatch.get(vo.getLogisticsChannelId());
            if (Objects.nonNull(channelDo)) {
                dto.setShippingService(channelDo.getChannelCode());
                if (StrUtil.isNotBlank(vo.getCustomizeChannelName())) {
                    dto.setShippingService(dto.getShippingService() + ":" + vo.getCustomizeChannelName());
                }
            } else {
                dto.setShippingService("自动选取价格最低的物流渠道");
            }
            dto.setOrderOrigin(OutboundOriginEnum.getDescByValue(vo.getOrderOrigin()));
            String address = (StrUtil.isBlank(vo.getProvinceName()) ? "" : vo.getProvinceName())
                             + (StrUtil.isBlank(vo.getCityName()) ? "" : vo.getCityName())
                             + (StrUtil.isBlank(vo.getAddress1()) ? "" : vo.getAddress1())
                             + (StrUtil.isBlank(vo.getAddress2()) ? "" : vo.getAddress2())
                             + (StrUtil.isBlank(vo.getHouseNum()) ? "" : vo.getHouseNum());
            dto.setAddress(address);
            //出库产品列表
            List<OrderOutboundProductDo> outboundProductDoList = outIdMatchProductList.get(vo.getId());
            dto.setSkuOutboundQty(outboundProductDoList.stream().map(item -> productIdMatch.getOrDefault(item.getProductId(), new ProductDo()).getSku() + "*" + item.getForecastQty()).collect(Collectors.joining("·")));
            if (TransferOutboundStatusEnum.four.getValue().equals(vo.getStatus())) {
                dto.setSkuActualOutboundQty(outboundProductDoList.stream().map(item -> productIdMatch.getOrDefault(item.getProductId(), new ProductDo()).getSku() + "*" + item.getOutboundQty()).collect(Collectors.joining("·")));
            }
            dtoList.add(dto);
        }
        ExcelUtils.exportExcel(dtoList, NormalOutExportBySimpleDto.class, "NormalOutbound_Export_" + DateUtil.format(DateUtil.date(), "yyyyMMddHHmmss"), new ExportParams(), response);
    }

    @Override
    public void normalOutboundExportWmsOmp(NormalOrderOutboundQueryBo bo, Long whId, Long tenantId, HttpServletResponse response) throws IOException {
        List<NormalOutboundVo> list = outboundDao.getNormalListPage(bo);
        if (CollUtil.isEmpty(list)) {
            throw new BizException(SysConstant.No_Data);
        }
        //出库单id
        List<Long> orderIdList = list.stream().map(NormalOutboundVo::getId).collect(Collectors.toList());
        Map<Long, WarehouseDo> whIdMatch = warehouseService.lambdaQuery()
                .in(WarehouseDo::getId, list.stream().map(NormalOutboundVo::getWhId).collect(Collectors.toList()))
                .list().stream().collect(Collectors.toMap(WarehouseDo::getId, val -> val));
        Map<Long, CompanyDo> companyIdMatch = companyService.lambdaQuery()
                .in(CompanyDo::getId, list.stream().map(NormalOutboundVo::getCompanyId).collect(Collectors.toList()))
                .list().stream().collect(Collectors.toMap(CompanyDo::getId, val -> val));
        Map<Long, LogisticsChannelDo> lcIdMatch = logisticsChannelService.lambdaQuery()
                .in(LogisticsChannelDo::getId, list.stream().map(NormalOutboundVo::getLogisticsChannelId).filter(Objects::nonNull).collect(Collectors.toList())).list().stream().collect(Collectors.toMap(LogisticsChannelDo::getId, val -> val));
        Map<Long, OrderOutboundPackageDo> outboundIdMatchPack = outboundPackageService.lambdaQuery()
                .in(OrderOutboundPackageDo::getOutboundId, orderIdList).list().stream().collect(Collectors.toMap(OrderOutboundPackageDo::getOutboundId, val -> val, (o1, o2) -> o1));
        Map<Long, List<OrderOutboundPackageMaterialDo>> packIdMatchMaterial = outboundPackageMaterialService.lambdaQuery()
                .in(OrderOutboundPackageMaterialDo::getPackageId, outboundIdMatchPack.values().stream().map(OrderOutboundPackageDo::getId).collect(Collectors.toList()))
                .list().stream().collect(Collectors.groupingBy(OrderOutboundPackageMaterialDo::getPackageId));
        Map<Long, List<OrderOutboundProductDo>> outboundIdMatchProductList = outboundProductService.lambdaQuery()
                .in(OrderOutboundProductDo::getOutboundId, orderIdList)
                .list().stream().collect(Collectors.groupingBy(OrderOutboundProductDo::getOutboundId));
        Map<Long, ProductDo> productIdMatch = productService.lambdaQuery()
                .in(ProductDo::getId, outboundIdMatchProductList.values().stream().flatMap(List::stream).map(OrderOutboundProductDo::getProductId).collect(Collectors.toList()))
                .list().stream().collect(Collectors.toMap(ProductDo::getId, val -> val));
        List<Long> productCategoryIdList = productIdMatch.values().stream().map(ProductDo::getCategoryId).filter(Objects::nonNull).collect(Collectors.toList());
        Map<Long, ProductCategoryDo> categoryIdMatch = new HashMap<>();
        if (CollUtil.isNotEmpty(productCategoryIdList)) {
            categoryIdMatch = productCategoryService.lambdaQuery()
                    .in(ProductCategoryDo::getId, productCategoryIdList)
                    .list().stream().collect(Collectors.toMap(ProductCategoryDo::getId, val -> val));
        }
        List<Long> storeIdList = list.stream().map(NormalOutboundVo::getStoreId).filter(Objects::nonNull).collect(Collectors.toList());
        Map<Long, PlatformStoreDo> storeIdMatchDo = new HashMap<>();
        if (CollUtil.isNotEmpty(storeIdList)) {
            storeIdMatchDo = platformStoreService.listByIds(storeIdList).stream().collect(Collectors.toMap(PlatformStoreDo::getId, val -> val));
        }
        WmsSystemEntity wmsSet = null;
        if (Objects.nonNull(whId)) {
            wmsSet = systemSetService.getWmsSet(whId);
        }
        OmpSystemEntity ompSet = null;
        if (Objects.nonNull(tenantId)) {
            ompSet = systemSetService.getOmpSet(tenantId);
        }
        List<NormalOutExportWmsDto> dtoList = new ArrayList<>(list.size());
        for (NormalOutboundVo vo : list) {
//            NormalOutExportWmsDto dto = new NormalOutExportWmsDto();
//            BeanUtils.copyProperties(vo, dto);
            NormalOutExportWmsDto dto = BeanConvert.INSTANCE.normalOutboundVoToNormalOutExportWmsDto(vo);
            WarehouseDo warehouseDo = whIdMatch.get(vo.getWhId());
            if (warehouseDo == null) {
                dto.setWarehouseNameCode("");
            } else {
                dto.setWarehouseNameCode(warehouseDo.getWhNameCn() + "(" + warehouseDo.getWhCode() + ")");
            }
            CompanyDo companyDo = companyIdMatch.get(vo.getCompanyId());
            if (companyDo == null) {
                dto.setCompanyNameCode("");
            } else {
                dto.setCompanyNameCode(companyDo.getCompanyName() + "(" + companyDo.getCompanyCode() + ")");
            }
            dto.setSalesPlatform(SalesPlatformEnum.getNameByVal(vo.getSalesPlatform()));
            PlatformStoreDo platformStoreDo = storeIdMatchDo.get(vo.getStoreId());
            if (platformStoreDo != null) {
                dto.setStoreName(platformStoreDo.getStoreName());
            }
            dto.setSignatureService(OutboundSignServiceEnum.getNameByVal(vo.getSignatureService()));
            if (Objects.isNull(vo.getInsureMoney())) {
                dto.setInsureMoney(BigDecimal.valueOf(0.00));
            }
            dto.setOrderVariety(OutboundVarietyTypeEnum.getDescByValue(vo.getOrderVarietyType()));
            LogisticsChannelDo channelDo = lcIdMatch.get(vo.getLogisticsChannelId());
            if (Objects.nonNull(channelDo)) {
                dto.setShippingService(channelDo.getChannelCode());
                if (StrUtil.isNotBlank(vo.getCustomizeChannelName())) {
                    dto.setShippingService(dto.getShippingService() + ":" + vo.getCustomizeChannelName());
                }
            } else {
                dto.setShippingService("自动选取价格最低的物流渠道");
            }
            if (Objects.nonNull(vo.getFaceSheetPrintTime())) {
                dto.setLabelPrintStatus("已打印");
            } else {
                dto.setLabelPrintStatus("未打印");
            }
            dto.setStatus(NormalOutboundStatusEnum.getDescByValue(vo.getStatus()));
            dto.setOrderOrigin(OutboundOriginEnum.getDescByValue(vo.getOrderOrigin()));
            //出库产品
            List<OrderOutboundProductDo> outboundProductDoList = outboundIdMatchProductList.get(vo.getId());
            dto.setSkuKind(Math.toIntExact(outboundProductDoList.stream().map(OrderOutboundProductDo::getProductId).distinct().count()));
            dto.setTotalQty(Math.toIntExact(outboundProductDoList.stream().map(OrderOutboundProductDo::getForecastQty).count()));
            dto.setPackageQty(1);
            dto.setCountry(CountryEnum.getNameEnByCountryRegionCode(vo.getCountryRegionCode()));
            OrderOutboundPackageDo packageDo = outboundIdMatchPack.get(vo.getId());
            String packMaterial = null;
            List<OrderOutboundPackageMaterialDo> packageMaterialDoList = packIdMatchMaterial.get(packageDo.getId());
            if (CollUtil.isNotEmpty(packageMaterialDoList)) {
                packMaterial = packageMaterialDoList.stream().map(item -> item.getMaterialCode() + "*" + item.getMaterialNum()).collect(Collectors.joining(","));
            }
            String packageSize = null;
            String packageWeight = null;
            if (StaticDict.OMP_UnitMeasurement.Cm.getValue().equals(Objects.nonNull(wmsSet) ? wmsSet.getUnitMeasurement().getUnit() : ompSet.getUnitMeasurement().getUnit())) {
                packageSize = packageDo.getLength() + "*" + packageDo.getWidth() + "*" + packageDo.getHeight() + "cm";
                packageWeight = packageDo.getWeight() + "kg";
            } else if (StaticDict.OMP_UnitMeasurement.In.getValue().equals(Objects.nonNull(wmsSet) ? wmsSet.getUnitMeasurement().getUnit() : ompSet.getUnitMeasurement().getUnit())) {
                packageSize = packageDo.getLengthBs() + "*" + packageDo.getWidthBs() + "*" + packageDo.getHeightBs() + "in";
                packageWeight = packageDo.getWeightBs() + "lb";
            }
            dto.setPackageSize(packageSize);
            dto.setPackageWeight(packageWeight);
            for (int i = 1; i <= outboundProductDoList.size(); i++) {
                OrderOutboundProductDo outboundProductDo = outboundProductDoList.get(i - 1);
                ProductDo productDo = productIdMatch.get(outboundProductDo.getProductId());
//                NormalOutExportWmsDto dtoTemp = new NormalOutExportWmsDto();
//                BeanUtils.copyProperties(dto, dtoTemp);
                NormalOutExportWmsDto dtoTemp = BeanConvert.INSTANCE.copyNormalOutExportWmsDto(dto);
                dtoTemp.setSku(productDo.getSku());
                dtoTemp.setJanCode(productDo.getJanCode());
                dtoTemp.setItf(productDo.getItf());
                dtoTemp.setProductName(productDo.getProductName());
                if (Objects.nonNull(productDo.getCategoryId())) {
                    ProductCategoryDo categoryDo = categoryIdMatch.get(productDo.getCategoryId());
                    if (categoryDo != null) {
                        dtoTemp.setProductCategory(categoryDo.getFullPathName());
                    }
                }
                dtoTemp.setOutboundQty(outboundProductDo.getForecastQty());
                if (StaticDict.OMP_UnitMeasurement.Cm.getValue().equals(Objects.nonNull(wmsSet) ? wmsSet.getUnitMeasurement().getUnit() : ompSet.getUnitMeasurement().getUnit())) {
                    dtoTemp.setLength(productDo.getWmsLength() != null ? productDo.getWmsLength() : productDo.getLength());
                    dtoTemp.setWidth(productDo.getWmsWidth() != null ? productDo.getWmsWidth() : productDo.getWidth());
                    dtoTemp.setHeight(productDo.getWmsHeight() != null ? productDo.getWmsHeight() : productDo.getHeight());
                    dtoTemp.setWeight(productDo.getWmsWeight() != null ? productDo.getWmsWeight() : productDo.getWeight());
                    dtoTemp.setMeasureUnit("cm");
                    dtoTemp.setWeightUnit("kg");
                } else if (StaticDict.OMP_UnitMeasurement.In.getValue().equals(Objects.nonNull(wmsSet) ? wmsSet.getUnitMeasurement().getUnit() : ompSet.getUnitMeasurement().getUnit())) {
                    dtoTemp.setLength(productDo.getWmsLengthBs() != null ? productDo.getWmsLengthBs() : productDo.getLengthBs());
                    dtoTemp.setWidth(productDo.getWmsWidthBs() != null ? productDo.getWmsWidthBs() : productDo.getWidthBs());
                    dtoTemp.setHeight(productDo.getWmsHeightBs() != null ? productDo.getWmsHeightBs() : productDo.getHeightBs());
                    dtoTemp.setWeight(productDo.getWmsWeightBs() != null ? productDo.getWmsWeightBs() : productDo.getWeightBs());
                    dtoTemp.setMeasureUnit("in");
                    dtoTemp.setWeightUnit("lb");
                }
                if (i == 1) {
                    dtoTemp.setPackageMaterialCodeQty(packMaterial);
                }
                dtoList.add(dtoTemp);
            }
        }
        ExportParams exportParams = new ExportParams();
        //设置样式
        exportParams.setStyle(ExcelExportTitleStyle.class);
        // 设置sheet的名称
        exportParams.setSheetName(bo.getSource() + "一件代发出库单导出");
        ExcelUtils.exportExcel(dtoList, NormalOutExportWmsDto.class, "NormalOutbound_Export_" + DateUtil.format(DateUtil.date(), "yyyyMMddHHmmss"), exportParams, response, "xls");
    }

    @Override
    public void transferOutboundExportOms(TransferOrderOutboundQueryBo bo, Long companyId, HttpServletResponse response) throws IOException {
        // 设置下载的Excel名称，以当前时间为文件后缀
        String dateTime = DateUtil.format(DateUtil.date(), "yyyyMMddHHmmss");
        String fileName = "TransferOutboundExport_" + dateTime + ".xls";
        // 设置响应输出的头类型
        response.setHeader("content-Type", "application/vnd.ms-excel");
        response.setHeader("Access-Control-Expose-Headers", "Content-Disposition");
        response.setHeader("Content-Disposition", "attachment;filename=" + fileName);
        List<TransferOutboundVo> list = outboundDao.getTransferListPage(bo);
        if (CollUtil.isEmpty(list)) {
            throw new BizException(SysConstant.No_Data);
        }
        //备货中转出库单（按SKU）
        Map<String, Object> transferSkuExportList = new HashMap<>(4);
        //备货中转出库单（按箱）
        Map<String, Object> transferBoxExportList = new HashMap<>(4);
        Map<Integer, List<TransferOutboundVo>> listByModeGroup = list.stream().collect(Collectors.groupingBy(TransferOutboundVo::getOutboundMode));
        List<TransferOutboundVo> voListBySku = listByModeGroup.getOrDefault(1, Collections.emptyList());
        List<TransferOutboundVo> voListByBox = listByModeGroup.getOrDefault(2, Collections.emptyList());
        //出库单id
        List<Long> orderIdList = list.stream().map(TransferOutboundVo::getId).collect(Collectors.toList());
        Map<Long, WarehouseDo> whIdMatch = warehouseService.lambdaQuery()
                .in(WarehouseDo::getId, list.stream().map(TransferOutboundVo::getWhId).collect(Collectors.toSet()))
                .list().stream().collect(Collectors.toMap(WarehouseDo::getId, val -> val));
        Map<Long, LogisticsChannelDo> lcIdMatch = logisticsChannelService.lambdaQuery()
                .in(LogisticsChannelDo::getId, list.stream().map(TransferOutboundVo::getLogisticsChannelId).collect(Collectors.toSet())).list().stream().collect(Collectors.toMap(LogisticsChannelDo::getId, val -> val));
        Map<Long, OrderOutboundPackageDo> outboundIdMatchPack = outboundPackageService.lambdaQuery()
                .in(OrderOutboundPackageDo::getOutboundId, orderIdList).list().stream().collect(Collectors.toMap(OrderOutboundPackageDo::getOutboundId, val -> val, (o1, o2) -> o1));
        Map<Long, List<OrderOutboundProductDo>> outboundIdMatchProductList = outboundProductService.lambdaQuery()
                .in(OrderOutboundProductDo::getOutboundId, orderIdList)
                .list().stream().collect(Collectors.groupingBy(OrderOutboundProductDo::getOutboundId));
        Map<Long, ProductDo> productIdMatch = productService.lambdaQuery()
                .in(ProductDo::getId, outboundIdMatchProductList.values().stream().flatMap(List::stream).map(OrderOutboundProductDo::getProductId).collect(Collectors.toSet()))
                .list().stream().collect(Collectors.toMap(ProductDo::getId, val -> val));
        Map<String, OrderBoxDo> boxTypeNoMatch = new HashMap<>(voListByBox.size() > 0 ? voListByBox.size() * 4 / 3 + 1 : 1);
        Set<String> boxTypeNoList = outboundIdMatchProductList.values().stream().flatMap(List::stream)
                .map(OrderOutboundProductDo::getBoxTypeNumber).collect(Collectors.toSet());
        if (CollUtil.isNotEmpty(boxTypeNoList)) {
            boxTypeNoMatch = boxService.lambdaQuery()
                    .in(OrderBoxDo::getBoxTypeNumber, boxTypeNoList)
                    .groupBy(OrderBoxDo::getBoxTypeNumber)
                    .list().stream().collect(Collectors.toMap(OrderBoxDo::getBoxTypeNumber, val -> val, (o1, o2) -> o1));
        }
        OmsSystemEntity omsSet = systemSetService.getOmsSet(companyId);
        ExportParams sheet1Params = new ExportParams();
        //设置样式
        sheet1Params.setStyle(ExcelExportTitleStyle.class);
        // 设置sheet的名称
        sheet1Params.setSheetName("按产品出库");
        List<TransferOutExportBySkuOmsDto> skuExportList = new ArrayList<>();
        for (TransferOutboundVo vo : voListBySku) {
//            TransferOutExportBySkuOmsDto dto = new TransferOutExportBySkuOmsDto();
//            BeanUtils.copyProperties(vo, dto);
            TransferOutExportBySkuOmsDto dto = BeanConvert.INSTANCE.transferOutVoToTransferOutExportBySkuOmsDto(vo);
            WarehouseDo warehouseDo = whIdMatch.get(vo.getWhId());
            if (warehouseDo == null) {
                dto.setWarehouseNameCode("");
            } else {
                dto.setWarehouseNameCode(warehouseDo.getWhNameCn() + "(" + warehouseDo.getWhCode() + ")");
            }
            dto.setStatus(NormalOutboundStatusEnum.getOmsDesc(vo.getStatus()));
            dto.setDestinationType(DestinationTypeEnum.getDescByVal(vo.getDestinationType()));
            LogisticsChannelDo channelDo = lcIdMatch.get(vo.getLogisticsChannelId());
            if (Objects.nonNull(channelDo)) {
                dto.setShippingService(channelDo.getChannelCode());
                if (StrUtil.isNotBlank(vo.getCustomizeChannelName())) {
                    dto.setShippingService(dto.getShippingService() + ":" + vo.getCustomizeChannelName());
                }
            } else {
                dto.setShippingService("自动选取价格最低的物流渠道");
            }
            dto.setCountry(CountryEnum.getNameEnByCountryRegionCode(vo.getCountryRegionCode()));
            OrderOutboundPackageDo packageDo = outboundIdMatchPack.get(vo.getId());
            String packageSize = null;
            String packageWeight = null;
            if (StaticDict.OMP_UnitMeasurement.Cm.getValue().equals(omsSet.getUnitMeasurement().getUnit())) {
                packageSize = packageDo.getLength() + "*" + packageDo.getWidth() + "*" + packageDo.getHeight() + "cm";
                packageWeight = packageDo.getWeight() + "kg";
            } else if (StaticDict.OMP_UnitMeasurement.In.getValue().equals(omsSet.getUnitMeasurement().getUnit())) {
                packageSize = packageDo.getLengthBs() + "*" + packageDo.getWidthBs() + "*" + packageDo.getHeightBs() + "in";
                packageWeight = packageDo.getWeightBs() + "lb";
            }
            dto.setPackageSize(packageSize);
            dto.setPackageWeight(packageWeight);
            //出库产品列表
            List<OrderOutboundProductDo> outboundProductDoList = outboundIdMatchProductList.get(vo.getId());
            for (OrderOutboundProductDo outboundProductDo : outboundProductDoList) {
                ProductDo productDo = productIdMatch.get(outboundProductDo.getProductId());
//                TransferOutExportBySkuOmsDto dtoTemp = new TransferOutExportBySkuOmsDto();
//                BeanUtils.copyProperties(dto, dtoTemp);
                TransferOutExportBySkuOmsDto dtoTemp = BeanConvert.INSTANCE.copyTransferOutExportBySkuOmsDto(dto);
                dtoTemp.setSku(productDo.getSku());
                dtoTemp.setJanCode(productDo.getJanCode());
                dtoTemp.setProductName(productDo.getProductName());
                dtoTemp.setOutboundQty(outboundProductDo.getForecastQty());
                dtoTemp.setActualOutboundQty(outboundProductDo.getOutboundQty());
                skuExportList.add(dtoTemp);
            }
        }
        // title的参数为ExportParams类型，目前仅仅在ExportParams中设置了sheetName
        transferSkuExportList.put("title", sheet1Params);
        // 模版导出对应得实体类型，即包含了List的对象
        transferSkuExportList.put("entity", TransferOutExportBySkuOmsDto.class);
        // sheet中要填充得数据
        transferSkuExportList.put("data", skuExportList);
        ExportParams sheet2Params = new ExportParams();
        //设置样式
        sheet2Params.setStyle(ExcelExportTitleStyle.class);
        // 设置sheet的名称
        sheet2Params.setSheetName("按箱出库");
        List<TransferOutExportByBoxOmsDto> boxExportList = new ArrayList<>();
        for (TransferOutboundVo vo : voListByBox) {
//            TransferOutExportByBoxOmsDto dto = new TransferOutExportByBoxOmsDto();
//            BeanUtils.copyProperties(vo, dto);
            TransferOutExportByBoxOmsDto dto = BeanConvert.INSTANCE.transferOutVoToTransferOutExportByBoxOmsDto(vo);
            WarehouseDo warehouseDo = whIdMatch.get(vo.getWhId());
            dto.setWarehouseNameCode(warehouseDo.getWhNameCn() + "(" + warehouseDo.getWhCode() + ")");
            dto.setStatus(TransferOutboundStatusEnum.getOmsDesc(vo.getStatus()));
            dto.setDestinationType(DestinationTypeEnum.getDescByVal(vo.getDestinationType()));
            LogisticsChannelDo channelDo = lcIdMatch.get(vo.getLogisticsChannelId());
            if (Objects.nonNull(channelDo)) {
                dto.setShippingService(channelDo.getChannelCode());
                if (StrUtil.isNotBlank(vo.getCustomizeChannelName())) {
                    dto.setShippingService(dto.getShippingService() + ":" + vo.getCustomizeChannelName());
                }
            } else {
                dto.setShippingService("自动选取价格最低的物流渠道");
            }
            dto.setCountry(CountryEnum.getNameEnByCountryRegionCode(vo.getCountryRegionCode()));
            OrderOutboundPackageDo packageDo = outboundIdMatchPack.get(vo.getId());
            String packageSize = null;
            String packageWeight = null;
            if (StaticDict.OMP_UnitMeasurement.Cm.getValue().equals(omsSet.getUnitMeasurement().getUnit())) {
                packageSize = packageDo.getLength() + "*" + packageDo.getWidth() + "*" + packageDo.getHeight() + "cm";
                packageWeight = packageDo.getWeight() + "kg";
            } else if (StaticDict.OMP_UnitMeasurement.In.getValue().equals(omsSet.getUnitMeasurement().getUnit())) {
                packageSize = packageDo.getLengthBs() + "*" + packageDo.getWidthBs() + "*" + packageDo.getHeightBs() + "in";
                packageWeight = packageDo.getWeightBs() + "lb";
            }
            dto.setPackageSize(packageSize);
            dto.setPackageWeight(packageWeight);
            //出库产品列表
            List<OrderOutboundProductDo> outboundProductDoList = outboundIdMatchProductList.get(vo.getId());
            for (int i = 1; i <= outboundProductDoList.size(); i++) {
                OrderOutboundProductDo outboundProductDo = outboundProductDoList.get(i - 1);
                ProductDo productDo = productIdMatch.get(outboundProductDo.getProductId());
                OrderBoxDo boxDo = boxTypeNoMatch.get(outboundProductDo.getBoxTypeNumber());
//                TransferOutExportByBoxOmsDto dtoTemp = new TransferOutExportByBoxOmsDto();
//                BeanUtils.copyProperties(dto, dtoTemp);
                TransferOutExportByBoxOmsDto dtoTemp = BeanConvert.INSTANCE.copyTransferOutExportByBoxOmsDto(dto);
                dtoTemp.setBoxTypeNumber(outboundProductDo.getBoxTypeNumber());
                dtoTemp.setCustomizeBarcode(boxDo.getCustomizeBarcode());
                String boxSize = null;
                String boxWeight = null;
                if (StaticDict.OMP_UnitMeasurement.Cm.getValue().equals(omsSet.getUnitMeasurement().getUnit())) {
                    if (Objects.nonNull(boxDo.getLength())) {
                        boxSize = boxDo.getLength() + "*" + boxDo.getWidth() + "*" + boxDo.getHeight() + "cm";
                        boxWeight = boxDo.getWeight() + "kg";
                    }
                } else if (StaticDict.OMP_UnitMeasurement.In.getValue().equals(omsSet.getUnitMeasurement().getUnit())) {
                    if (Objects.nonNull(boxDo.getLengthBs())) {
                        boxSize = boxDo.getLengthBs() + "*" + boxDo.getWidthBs() + "*" + boxDo.getHeightBs() + "in";
                        boxWeight = boxDo.getWeightBs() + "lb";
                    }
                }
                dtoTemp.setBoxSize(boxSize);
                dtoTemp.setBoxWeight(boxWeight);
                if (i == 1) {
                    dtoTemp.setOutboundBoxQty(outboundProductDo.getForecastBoxQty());
                    dtoTemp.setActualOutboundBoxQty(outboundProductDo.getOutboundBoxQty());
                }
                dtoTemp.setSku(productDo.getSku());
                dtoTemp.setJanCode(productDo.getJanCode());
                dtoTemp.setPerBoxQty(outboundProductDo.getForecastQty() / outboundProductDo.getForecastBoxQty());
                boxExportList.add(dtoTemp);
            }
        }
        // title的参数为ExportParams类型，目前仅仅在ExportParams中设置了sheetName
        transferBoxExportList.put("title", sheet2Params);
        // 模版导出对应得实体类型，即包含了List的对象
        transferBoxExportList.put("entity", TransferOutExportByBoxOmsDto.class);
        // sheet中要填充得数据
        transferBoxExportList.put("data", boxExportList);
        //添加表
        List<Map<String, Object>> sheetsList = new ArrayList<>(2);
        sheetsList.add(transferSkuExportList);
        sheetsList.add(transferBoxExportList);
        //创建excel文件的方法
        Workbook workbook = ExcelExportUtil.exportExcel(sheetsList, ExcelType.HSSF);
        //通过response输出流直接输入给客户端
        ServletOutputStream outputStream = response.getOutputStream();
        workbook.write(outputStream);
        outputStream.flush();
        outputStream.close();
    }

    @Override
    public void transferOutboundExportBySimple(TransferOrderOutboundQueryBo bo, HttpServletResponse response) throws IOException {
        List<TransferOutboundVo> list = outboundDao.getTransferListPage(bo);
        if (CollUtil.isEmpty(list)) {
            throw new BizException(SysConstant.No_Data);
        }
        Map<Long, CompanyDo> companyIdMatch = companyService.lambdaQuery()
                .in(CompanyDo::getId, list.stream().map(TransferOutboundVo::getCompanyId).collect(Collectors.toSet()))
                .list().stream().collect(Collectors.toMap(CompanyDo::getId, val -> val));
        Map<Long, WarehouseDo> whIdMatch = warehouseService.lambdaQuery()
                .in(WarehouseDo::getId, list.stream().map(TransferOutboundVo::getWhId).collect(Collectors.toSet()))
                .list().stream().collect(Collectors.toMap(WarehouseDo::getId, val -> val));
        Map<Long, LogisticsChannelDo> lcIdMatch = logisticsChannelService.lambdaQuery()
                .in(LogisticsChannelDo::getId, list.stream().map(TransferOutboundVo::getLogisticsChannelId).collect(Collectors.toSet())).list().stream().collect(Collectors.toMap(LogisticsChannelDo::getId, val -> val));
        Map<Long, List<OrderOutboundProductDo>> outIdMatchProductList = outboundProductService.lambdaQuery()
                .in(OrderOutboundProductDo::getOutboundId, list.stream().map(TransferOutboundVo::getId).collect(Collectors.toList())).list().stream().collect(Collectors.groupingBy(OrderOutboundProductDo::getOutboundId));
        Map<Long, ProductDo> productIdMatch = productService.lambdaQuery()
                .in(ProductDo::getId, outIdMatchProductList.values().stream().flatMap(List::stream).map(OrderOutboundProductDo::getProductId).collect(Collectors.toSet()))
                .list().stream().collect(Collectors.toMap(ProductDo::getId, val -> val));
        List<TransferOutExportBySimpleDto> dtoList = new ArrayList<>();
        for (TransferOutboundVo vo : list) {
//            TransferOutExportBySimpleDto dto = new TransferOutExportBySimpleDto();
//            BeanUtils.copyProperties(vo, dto);
            TransferOutExportBySimpleDto dto = BeanConvert.INSTANCE.transferOutVoToTransferOutExportBySimpleDto(vo);
            CompanyDo companyDo = companyIdMatch.get(vo.getCompanyId());
            if (companyDo == null) {
                dto.setCompanyNameCode("");
            } else {
                dto.setCompanyNameCode(companyDo.getName() + "(" + companyDo.getCompanyCode() + ")");
            }
            WarehouseDo warehouseDo = whIdMatch.get(vo.getWhId());
            if (warehouseDo == null) {
                dto.setWarehouseNameCode("");
            } else {
                dto.setWarehouseNameCode(warehouseDo.getWhNameCn() + "(" + warehouseDo.getWhCode() + ")");
            }
            if ("oms".equals(bo.getSource())) {
                dto.setStatus(TransferOutboundStatusEnum.getOmsDesc(vo.getStatus()));
            } else {
                dto.setStatus(TransferOutboundStatusEnum.getDescByValue(vo.getStatus()));
            }
            dto.setDestinationType(DestinationTypeEnum.getDescByVal(vo.getDestinationType()));
            LogisticsChannelDo channelDo = lcIdMatch.get(vo.getLogisticsChannelId());
            if (Objects.nonNull(channelDo)) {
                dto.setShippingService(channelDo.getChannelCode());
                if (StrUtil.isNotBlank(vo.getCustomizeChannelName())) {
                    dto.setShippingService(dto.getShippingService() + ":" + vo.getCustomizeChannelName());
                }
            } else {
                dto.setShippingService("自动选取价格最低的物流渠道");
            }
            String address = (StrUtil.isBlank(vo.getProvinceName()) ? "" : vo.getProvinceName())
                             + (StrUtil.isBlank(vo.getCityName()) ? "" : vo.getCityName())
                             + (StrUtil.isBlank(vo.getAddress1()) ? "" : vo.getAddress1())
                             + (StrUtil.isBlank(vo.getAddress2()) ? "" : vo.getAddress2())
                             + (StrUtil.isBlank(vo.getHouseNum()) ? "" : vo.getHouseNum());
            dto.setAddress(address);
            Integer outboundMode = vo.getOutboundMode();
            List<OrderOutboundProductDo> outboundProductDoList = outIdMatchProductList.get(vo.getId());
            if (Objects.equals(outboundMode, 1)) {
                dto.setSkuOutboundQty(outboundProductDoList.stream().map(item -> productIdMatch.getOrDefault(item.getProductId(), new ProductDo()).getSku() + "*" + item.getForecastQty()).collect(Collectors.joining("·")));
                if (TransferOutboundStatusEnum.four.getValue().equals(vo.getStatus())) {
                    dto.setSkuActualOutboundQty(outboundProductDoList.stream().map(item -> productIdMatch.getOrDefault(item.getProductId(), new ProductDo()).getSku() + "*" + item.getOutboundQty()).collect(Collectors.joining("·")));
                }
            } else if (Objects.equals(outboundMode, 2)) {
                outboundProductDoList = new ArrayList<>(outboundProductDoList.stream().collect(Collectors.toMap(OrderOutboundProductDo::getBoxTypeNumber, val -> val, (o1, o2) -> o1)).values());
                dto.setSkuOutboundQty(outboundProductDoList.stream().map(item -> item.getBoxTypeNumber() + "*" + item.getForecastBoxQty()).collect(Collectors.joining("·")));
                if (TransferOutboundStatusEnum.four.getValue().equals(vo.getStatus())) {
                    dto.setSkuActualOutboundQty(outboundProductDoList.stream().map(item -> item.getBoxTypeNumber() + "*" + item.getOutboundBoxQty()).collect(Collectors.joining("·")));
                }
            }
            dtoList.add(dto);
        }
        ExcelUtils.exportExcel(dtoList, TransferOutExportBySimpleDto.class, "TransferOutbound_Export_" + DateUtil.format(DateUtil.date(), "yyyyMMddHHmmss"), new ExportParams(), response);
    }

    @Override
    public void transferOutboundExportWmsOmp(TransferOrderOutboundQueryBo bo, Long whId, Long tenantId, HttpServletResponse response) throws IOException {
        // 设置下载的Excel名称，以当前时间为文件后缀
        String dateTime = DateUtil.format(DateUtil.date(), "yyyyMMddHHmmss");
        String fileName = "TransferOutboundExport_" + dateTime + ".xls";
        // 设置响应输出的头类型
        response.setHeader("content-Type", "application/vnd.ms-excel");
        response.setHeader("Access-Control-Expose-Headers", "Content-Disposition");
        response.setHeader("Content-Disposition", "attachment;filename=" + fileName);
        List<TransferOutboundVo> list = outboundDao.getTransferListPage(bo);
        if (CollUtil.isEmpty(list)) {
            throw new BizException(SysConstant.No_Data);
        }
        //备货中转出库单（按SKU）
        Map<String, Object> transferSkuExportList = new HashMap<>(4);
        //备货中转出库单（按箱）
        Map<String, Object> transferBoxExportList = new HashMap<>(4);
        Map<Integer, List<TransferOutboundVo>> listByModeGroup = list.stream().collect(Collectors.groupingBy(TransferOutboundVo::getOutboundMode));
        List<TransferOutboundVo> voListBySku = listByModeGroup.getOrDefault(1, Collections.emptyList());
        List<TransferOutboundVo> voListByBox = listByModeGroup.getOrDefault(2, Collections.emptyList());
        //出库单id
        List<Long> orderIdList = list.stream().map(TransferOutboundVo::getId).collect(Collectors.toList());
        Map<Long, CompanyDo> companyIdMatch = companyService.lambdaQuery()
                .in(CompanyDo::getId, list.stream().map(TransferOutboundVo::getCompanyId).collect(Collectors.toSet()))
                .list().stream().collect(Collectors.toMap(CompanyDo::getId, val -> val));
        Map<Long, WarehouseDo> whIdMatch = warehouseService.lambdaQuery()
                .in(WarehouseDo::getId, list.stream().map(TransferOutboundVo::getWhId).collect(Collectors.toSet()))
                .list().stream().collect(Collectors.toMap(WarehouseDo::getId, val -> val));
        Map<Long, LogisticsChannelDo> lcIdMatch = logisticsChannelService.lambdaQuery()
                .in(LogisticsChannelDo::getId, list.stream().map(TransferOutboundVo::getLogisticsChannelId).collect(Collectors.toSet())).list().stream().collect(Collectors.toMap(LogisticsChannelDo::getId, val -> val));
        Map<Long, OrderOutboundPackageDo> outboundIdMatchPack = outboundPackageService.lambdaQuery()
                .in(OrderOutboundPackageDo::getOutboundId, orderIdList).list().stream().collect(Collectors.toMap(OrderOutboundPackageDo::getOutboundId, val -> val, (o1, o2) -> o1));
        Map<Long, List<OrderOutboundProductDo>> outboundIdMatchProductList = outboundProductService.lambdaQuery()
                .in(OrderOutboundProductDo::getOutboundId, orderIdList)
                .list().stream().collect(Collectors.groupingBy(OrderOutboundProductDo::getOutboundId));
        Map<Long, ProductDo> productIdMatch = productService.lambdaQuery()
                .in(ProductDo::getId, outboundIdMatchProductList.values().stream().flatMap(List::stream).map(OrderOutboundProductDo::getProductId).collect(Collectors.toSet()))
                .list().stream().collect(Collectors.toMap(ProductDo::getId, val -> val));
        Map<String, OrderBoxDo> boxTypeNoMatch = new HashMap<>(voListByBox.size() > 0 ? voListByBox.size() * 4 / 3 + 1 : 1);
        Set<String> boxTypeNoList = outboundIdMatchProductList.values().stream().flatMap(List::stream)
                .map(OrderOutboundProductDo::getBoxTypeNumber).collect(Collectors.toSet());
        if (CollUtil.isNotEmpty(boxTypeNoList)) {
            boxTypeNoMatch = boxService.lambdaQuery()
                    .in(OrderBoxDo::getBoxTypeNumber, boxTypeNoList)
                    .list().stream().collect(Collectors.toMap(OrderBoxDo::getBoxTypeNumber, val -> val, (o1, o2) -> o1));
        }
        WmsSystemEntity wmsSet = null;
        OmpSystemEntity ompSet = null;
        if (Objects.nonNull(whId)) {
            wmsSet = systemSetService.getWmsSet(whId);
        } else if (Objects.nonNull(tenantId)) {
            ompSet = systemSetService.getOmpSet(tenantId);
        }
        ExportParams sheet1Params = new ExportParams();
        //设置样式
        sheet1Params.setStyle(ExcelExportTitleStyle.class);
        // 设置sheet的名称
        sheet1Params.setSheetName("按产品出库");
        List<TransferOutExportBySkuWmsOmpDto> skuExportList = new ArrayList<>();
        for (TransferOutboundVo vo : voListBySku) {
//            TransferOutExportBySkuWmsOmpDto dto = new TransferOutExportBySkuWmsOmpDto();
//            BeanUtils.copyProperties(vo, dto);
            TransferOutExportBySkuWmsOmpDto dto = BeanConvert.INSTANCE.transferOutVoToTransferOutExportBySkuWmsOmpDto(vo);
            CompanyDo companyDo = companyIdMatch.get(vo.getCompanyId());
            if (companyDo == null) {
                dto.setCompanyNameCode("");
            } else {
                dto.setCompanyNameCode(companyDo.getName() + "(" + companyDo.getCompanyCode() + ")");
            }
            WarehouseDo warehouseDo = whIdMatch.get(vo.getWhId());
            if (warehouseDo == null) {
                dto.setWarehouseNameCode("");
            } else {
                dto.setWarehouseNameCode(warehouseDo.getWhNameCn() + "(" + warehouseDo.getWhCode() + ")");
            }
            dto.setStatus(TransferOutboundStatusEnum.getDescByValue(vo.getStatus()));
            dto.setDestinationType(DestinationTypeEnum.getDescByVal(vo.getDestinationType()));
            LogisticsChannelDo channelDo = lcIdMatch.get(vo.getLogisticsChannelId());
            if (Objects.nonNull(channelDo)) {
                dto.setShippingService(channelDo.getChannelCode());
                if (StrUtil.isNotBlank(vo.getCustomizeChannelName())) {
                    dto.setShippingService(dto.getShippingService() + ":" + vo.getCustomizeChannelName());
                }
            } else {
                dto.setShippingService("自动选取价格最低的物流渠道");
            }
            dto.setCountry(CountryEnum.getNameEnByCountryRegionCode(vo.getCountryRegionCode()));
            OrderOutboundPackageDo packageDo = outboundIdMatchPack.get(vo.getId());
            String packageSize = null;
            String packageWeight = null;
            if (StaticDict.OMP_UnitMeasurement.Cm.getValue().equals(Objects.nonNull(wmsSet) ? wmsSet.getUnitMeasurement().getUnit() : ompSet.getUnitMeasurement().getUnit())) {
                packageSize = packageDo.getLength() + "*" + packageDo.getWidth() + "*" + packageDo.getHeight() + "cm";
                packageWeight = packageDo.getWeight() + "kg";
            } else if (StaticDict.OMP_UnitMeasurement.In.getValue().equals(Objects.nonNull(wmsSet) ? wmsSet.getUnitMeasurement().getUnit() : ompSet.getUnitMeasurement().getUnit())) {
                packageSize = packageDo.getLengthBs() + "*" + packageDo.getWidthBs() + "*" + packageDo.getHeightBs() + "in";
                packageWeight = packageDo.getWeightBs() + "lb";
            }
            dto.setPackageSize(packageSize);
            dto.setPackageWeight(packageWeight);
            //出库产品列表
            List<OrderOutboundProductDo> outboundProductDoList = outboundIdMatchProductList.get(vo.getId());
            for (OrderOutboundProductDo outboundProductDo : outboundProductDoList) {
                ProductDo productDo = productIdMatch.get(outboundProductDo.getProductId());
//                TransferOutExportBySkuWmsOmpDto dtoTemp = new TransferOutExportBySkuWmsOmpDto();
//                BeanUtils.copyProperties(dto, dtoTemp);
                TransferOutExportBySkuWmsOmpDto dtoTemp = BeanConvert.INSTANCE.copyTransferOutExportBySkuWmsOmpDto(dto);
                dtoTemp.setSku(productDo.getSku());
                dtoTemp.setJanCode(productDo.getJanCode());
                dtoTemp.setProductName(productDo.getProductName());
                dtoTemp.setOutboundQty(outboundProductDo.getForecastQty());
                dtoTemp.setActualOutboundQty(outboundProductDo.getOutboundQty());
                skuExportList.add(dtoTemp);
            }
        }
        // title的参数为ExportParams类型，目前仅仅在ExportParams中设置了sheetName
        transferSkuExportList.put("title", sheet1Params);
        // 模版导出对应得实体类型，即包含了List的对象
        transferSkuExportList.put("entity", TransferOutExportBySkuWmsOmpDto.class);
        // sheet中要填充得数据
        transferSkuExportList.put("data", skuExportList);
        ExportParams sheet2Params = new ExportParams();
        //设置样式
        sheet2Params.setStyle(ExcelExportTitleStyle.class);
        // 设置sheet的名称
        sheet2Params.setSheetName("按箱出库");
        List<TransferOutExportByBoxWmsOmpDto> boxExportList = new ArrayList<>();
        for (TransferOutboundVo vo : voListByBox) {
//            TransferOutExportByBoxWmsOmpDto dto = new TransferOutExportByBoxWmsOmpDto();
//            BeanUtils.copyProperties(vo, dto);
            TransferOutExportByBoxWmsOmpDto dto = BeanConvert.INSTANCE.transferOutVoToTransferOutExportByBoxWmsOmpDto(vo);
            CompanyDo companyDo = companyIdMatch.get(vo.getCompanyId());
            dto.setCompanyNameCode(companyDo.getName() + "(" + companyDo.getCompanyCode() + ")");
            WarehouseDo warehouseDo = whIdMatch.get(vo.getWhId());
            dto.setWarehouseNameCode(warehouseDo.getWhNameCn() + "(" + warehouseDo.getWhCode() + ")");
            dto.setStatus(NormalOutboundStatusEnum.getDescByValue(vo.getStatus()));
            dto.setDestinationType(DestinationTypeEnum.getDescByVal(vo.getDestinationType()));
            LogisticsChannelDo channelDo = lcIdMatch.get(vo.getLogisticsChannelId());
            if (Objects.nonNull(channelDo)) {
                dto.setShippingService(channelDo.getChannelCode());
                if (StrUtil.isNotBlank(vo.getCustomizeChannelName())) {
                    dto.setShippingService(dto.getShippingService() + ":" + vo.getCustomizeChannelName());
                }
            } else {
                dto.setShippingService("自动选取价格最低的物流渠道");
            }
            dto.setCountry(CountryEnum.getNameEnByCountryRegionCode(vo.getCountryRegionCode()));
            OrderOutboundPackageDo packageDo = outboundIdMatchPack.get(vo.getId());
            String packageSize = null;
            String packageWeight = null;
            if (StaticDict.OMP_UnitMeasurement.Cm.getValue().equals(Objects.nonNull(wmsSet) ? wmsSet.getUnitMeasurement().getUnit() : ompSet.getUnitMeasurement().getUnit())) {
                packageSize = packageDo.getLength() + "*" + packageDo.getWidth() + "*" + packageDo.getHeight() + "cm";
                packageWeight = packageDo.getWeight() + "kg";
            } else if (StaticDict.OMP_UnitMeasurement.In.getValue().equals(Objects.nonNull(wmsSet) ? wmsSet.getUnitMeasurement().getUnit() : ompSet.getUnitMeasurement().getUnit())) {
                packageSize = packageDo.getLengthBs() + "*" + packageDo.getWidthBs() + "*" + packageDo.getHeightBs() + "in";
                packageWeight = packageDo.getWeightBs() + "lb";
            }
            dto.setPackageSize(packageSize);
            dto.setPackageWeight(packageWeight);
            //出库产品列表
            List<OrderOutboundProductDo> outboundProductDoList = outboundIdMatchProductList.get(vo.getId());
            for (int i = 1; i <= outboundProductDoList.size(); i++) {
                OrderOutboundProductDo outboundProductDo = outboundProductDoList.get(i - 1);
                ProductDo productDo = productIdMatch.get(outboundProductDo.getProductId());
                OrderBoxDo boxDo = boxTypeNoMatch.get(outboundProductDo.getBoxTypeNumber());
//                TransferOutExportByBoxWmsOmpDto dtoTemp = new TransferOutExportByBoxWmsOmpDto();
//                BeanUtils.copyProperties(dto, dtoTemp);
                TransferOutExportByBoxWmsOmpDto dtoTemp = BeanConvert.INSTANCE.copyTransferOutExportByBoxWmsOmpDto(dto);
                dtoTemp.setBoxTypeNumber(outboundProductDo.getBoxTypeNumber());
                dtoTemp.setCustomizeBarcode(boxDo.getCustomizeBarcode());
                String boxSize = null;
                String boxWeight = null;
                if (StaticDict.OMP_UnitMeasurement.Cm.getValue().equals(Objects.nonNull(wmsSet) ? wmsSet.getUnitMeasurement().getUnit() : ompSet.getUnitMeasurement().getUnit())) {
                    if (Objects.nonNull(boxDo.getLength())) {
                        boxSize = boxDo.getLength() + "*" + boxDo.getWidth() + "*" + boxDo.getHeight() + "cm";
                        boxWeight = boxDo.getWeight() + "kg";
                    }
                } else if (StaticDict.OMP_UnitMeasurement.In.getValue().equals(Objects.nonNull(wmsSet) ? wmsSet.getUnitMeasurement().getUnit() : ompSet.getUnitMeasurement().getUnit())) {
                    if (Objects.nonNull(boxDo.getLengthBs())) {
                        boxSize = boxDo.getLengthBs() + "*" + boxDo.getWidthBs() + "*" + boxDo.getHeightBs() + "in";
                        boxWeight = boxDo.getWeightBs() + "lb";
                    }
                }
                dtoTemp.setBoxSize(boxSize);
                dtoTemp.setBoxWeight(boxWeight);
                if (i == 1) {
                    dtoTemp.setOutboundBoxQty(outboundProductDo.getForecastBoxQty());
                    dtoTemp.setActualOutboundBoxQty(outboundProductDo.getOutboundBoxQty());
                }
                dtoTemp.setSku(productDo.getSku());
                dtoTemp.setJanCode(productDo.getJanCode());
                dtoTemp.setPerBoxQty(outboundProductDo.getForecastQty() / outboundProductDo.getForecastBoxQty());
                boxExportList.add(dtoTemp);
            }
        }
        // title的参数为ExportParams类型，目前仅仅在ExportParams中设置了sheetName
        transferBoxExportList.put("title", sheet2Params);
        // 模版导出对应得实体类型，即包含了List的对象
        transferBoxExportList.put("entity", TransferOutExportByBoxWmsOmpDto.class);
        // sheet中要填充得数据
        transferBoxExportList.put("data", boxExportList);
        //添加表
        List<Map<String, Object>> sheetsList = new ArrayList<>(2);
        sheetsList.add(transferSkuExportList);
        sheetsList.add(transferBoxExportList);
        //创建excel文件的方法
        Workbook workbook = ExcelExportUtil.exportExcel(sheetsList, ExcelType.HSSF);
        //通过response输出流直接输入给客户端
        ServletOutputStream outputStream = response.getOutputStream();
        workbook.write(outputStream);
        outputStream.flush();
        outputStream.close();
    }

    @Override
    public void getImportUpdateTrackingNumberTemplate(HttpServletResponse response) throws IOException {
        TemplateExportParams params = new TemplateExportParams(
                "excelTemplates/TrackingNumberImport.xlsx", true);
        response.setCharacterEncoding("UTF-8");
        response.setHeader("content-Type", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
        response.setHeader("Access-Control-Expose-Headers", "Content-Disposition");
        response.setHeader("Content-Disposition", "attachment;filename=TrackingNumberImport.xlsx");
        //导出数据
        try (Workbook book = ExcelExportUtil.exportExcel(params, this.buildUpdateTrackingNumberTemplateSheetMap())) {
            book.write(response.getOutputStream());
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            response.getOutputStream().flush();
            response.getOutputStream().close();
        }
    }


    @Override
    public CommonBatchOperationVo importFbaAllocateOutbound(MultipartFile file, Long tenantId, Long userId, String username, Long companyId) {
        CommonBatchOperationVo res = new CommonBatchOperationVo();
        List<AllocateOutImportByFbaDto> importDtoList;
        try {
            importDtoList = ExcelUtils.importExcel(file, 1, 0, AllocateOutImportByFbaDto.class);
        } catch (Exception e) {
            throw new BizException(SysConstant.Excel_Template_Error);
        }
        Set<Integer> exceptionNoSet = importDtoList.stream().filter(
                i -> Objects.isNull(i.getNo())
                     || StrUtil.isBlank(i.getDeliveryWarehouseCode())
                     || Objects.isNull(i.getFbaAddressBookId())
                     || StrUtil.isBlank(i.getFbaStoreCode())
                     || StrUtil.isBlank(i.getPlatformSku())
                     || Objects.isNull(i.getQuantity())
        ).map(AllocateOutImportByFbaDto::getNo).collect(Collectors.toSet());
        int oldSize = importDtoList.size();
        res.setTotal(importDtoList.size());
        importDtoList = importDtoList.stream().filter(i -> !exceptionNoSet.contains(i.getNo())).collect(Collectors.toList());
        int newSize = importDtoList.size();
        if (oldSize != newSize) {
            HashMap<String, Object> map = new HashMap<>(4);
            map.put("code", StrUtil.join(",", exceptionNoSet));
            map.put("errorMsg", StringsUtil.createI18Message(SysConstant.Import_Required_Field_Blank));
            res.setFailed(res.getFailed() + oldSize - newSize);
            res.getFailedList().add(map);
        }
        if (importDtoList.isEmpty()) {
            return res;
        }
        Map<String, WarehouseDo> whCodeMatch = warehouseService.lambdaQuery()
                .eq(WarehouseDo::getTenantId, tenantId)
                .list().stream().collect(Collectors.toMap(WarehouseDo::getWhCode, val -> val));
        List<Long> fbaAddressBookIdList = addressBookService.listObjs(
                Wrappers.<AddressBookDo>lambdaQuery()
                        .select(AddressBookDo::getId)
                        .eq(AddressBookDo::getTenantId, tenantId)
                        .eq(AddressBookDo::getCompanyId, companyId)
                        .eq(AddressBookDo::getAddressType, AddressBookTypeEnum.one.getValue()),
                obj -> ((BigInteger) obj).longValue()
        );
        Map<String, LogisticsChannelDo> channelCodeMatch = logisticsChannelService.lambdaQuery().eq(LogisticsChannelDo::getTenantId, tenantId).list().stream().collect(Collectors.toMap(LogisticsChannelDo::getChannelCode, val -> val, (k1, k2) -> k1));
        Map<String, PlatformStoreDo> amazonStoreCodeMatch = platformStoreService.lambdaQuery().eq(PlatformStoreDo::getPlatformCode, "amazon").eq(Objects.nonNull(companyId), PlatformStoreDo::getCompanyId, companyId).eq(Objects.nonNull(tenantId), PlatformStoreDo::getTenantId, tenantId).list().stream().collect(Collectors.toMap(PlatformStoreDo::getStoreCode, val -> val, (k1, k2) -> k1));
        if (CollUtil.isEmpty(amazonStoreCodeMatch)) {
            //系统不存在amazon平台店铺
            throw new BizException("System Does Not Exist Amazon Platform Store");
        }
        Set<PlatformStoreDo> amazonStoreSet = importDtoList.stream().map(item -> amazonStoreCodeMatch.get(item.getFbaStoreCode())).filter(Objects::nonNull).collect(Collectors.toSet());
        if (CollUtil.isEmpty(amazonStoreSet)) {
            HashMap<String, Object> map = new HashMap<>(4);
            map.put("code", "All");
            map.put("errorMsg", "Amazon Store Not Found");
            res.setFailed(res.getFailed() + newSize);
            res.getFailedList().add(map);
            return res;
        }
        Map<String, PlatformProductDo> storeIdAndPlatSkuMatchDo = platformProductService.lambdaQuery()
                .eq(PlatformProductDo::getTenantId, tenantId)
                .eq(PlatformProductDo::getCompanyId, companyId)
                .in(PlatformProductDo::getStoreId, amazonStoreSet.stream().map(PlatformStoreDo::getId).collect(Collectors.toList()))
                .in(PlatformProductDo::getPlatformSku, importDtoList.stream().map(AllocateOutImportByFbaDto::getPlatformSku).collect(Collectors.toList()))
                .list().stream().collect(Collectors.toMap(key -> key.getStoreId() + "_" + key.getPlatformSku(), val -> val));
        if (CollUtil.isEmpty(storeIdAndPlatSkuMatchDo)) {
            HashMap<String, Object> map = new HashMap<>(4);
            map.put("code", "All");
            map.put("errorMsg", "Platform Sku Not Found");
            res.setFailed(res.getFailed() + newSize);
            res.getFailedList().add(map);
            return res;
        }
        Map<Long, List<PlatformProductMappingDo>> platProductIdMatchMappings = platformProductMappingService.lambdaQuery()
                .in(PlatformProductMappingDo::getPlatformProductId, storeIdAndPlatSkuMatchDo.values().stream().map(PlatformProductDo::getId).collect(Collectors.toSet()))
                .list().stream().collect(Collectors.groupingBy(PlatformProductMappingDo::getPlatformProductId));
        Map<Long, ProductDo> productIdMatch = new HashMap<>();
        if (CollUtil.isNotEmpty(platProductIdMatchMappings)) {
            productIdMatch = productService.lambdaQuery()
                    .in(ProductDo::getId, platProductIdMatchMappings.values().stream().flatMap(Collection::stream).map(PlatformProductMappingDo::getSystemProductId).collect(Collectors.toList()))
                    .list().stream().collect(Collectors.toMap(ProductDo::getId, val -> val));
        }
        Map<Integer, List<AllocateOutImportByFbaDto>> dtoListGroup = importDtoList.stream().collect(Collectors.groupingBy(AllocateOutImportByFbaDto::getNo));
        for (Map.Entry<Integer, List<AllocateOutImportByFbaDto>> entry : dtoListGroup.entrySet()) {
            List<AllocateOutImportByFbaDto> dtoList = entry.getValue();
            AllocateOutImportByFbaDto dto = dtoList.get(0);
            if (whCodeMatch.get(dto.getDeliveryWarehouseCode()) == null) {
                HashMap<String, Object> map = new HashMap<>(4);
                map.put("code", dto.getNo());
                map.put("errorMsg", "Delivery Warehouse Code/发货仓库代码[" + dto.getDeliveryWarehouseCode() + "] Wrong,Please check!");
                res.setFailed(res.getFailed() + dtoList.size());
                res.getFailedList().add(map);
                continue;
            }
            if (!fbaAddressBookIdList.contains(dto.getFbaAddressBookId())) {
                HashMap<String, Object> map = new HashMap<>(4);
                map.put("code", dto.getNo());
                map.put("errorMsg", "Fba Destination Address/ Fba目的地编码[" + dto.getFbaAddressBookId() + "] Wrong,Please check!");
                res.setFailed(res.getFailed() + dtoList.size());
                res.getFailedList().add(map);
                continue;
            }
            if (StrUtil.isNotBlank(dto.getShippingServiceCode())) {
                LogisticsChannelDo logisticsChannelDo = channelCodeMatch.get(dto.getShippingServiceCode());
                if (logisticsChannelDo == null) {
                    HashMap<String, Object> map = new HashMap<>(4);
                    map.put("code", dto.getNo());
                    map.put("errorMsg", "Shipping Service Code/物流渠道代码[" + dto.getShippingServiceCode() + "] Wrong,Please check!");
                    res.setFailed(res.getFailed() + dtoList.size());
                    res.getFailedList().add(map);
                    continue;
                }
            }
            PlatformStoreDo storeDo = amazonStoreCodeMatch.get(dto.getFbaStoreCode());
            if (storeDo == null) {
                HashMap<String, Object> map = new HashMap<>(4);
                map.put("code", dto.getNo());
                map.put("errorMsg", "FBA Store Code/ FBA店铺编码[" + dto.getFbaStoreCode() + "] Wrong,Please check!");
                res.setFailed(res.getFailed() + dtoList.size());
                res.getFailedList().add(map);
                continue;
            }
            List<AllocatePlanItemDo> planItemDoList = new ArrayList<>();
            for (AllocateOutImportByFbaDto dtoItem : dtoList) {
                PlatformProductDo platformProductDo = storeIdAndPlatSkuMatchDo.get(storeDo.getId() + "_" + dtoItem.getPlatformSku());
                if (platformProductDo == null) {
                    HashMap<String, Object> map = new HashMap<>(4);
                    map.put("code", dto.getNo());
                    map.put("errorMsg", "Platform Sku/ 平台sku[" + dtoItem.getPlatformSku() + "] Not Found,Please check!");
                    res.setFailed(res.getFailed() + dtoList.size());
                    res.getFailedList().add(map);
                    planItemDoList.clear();
                    break;
                }
                if (Objects.isNull(dtoItem.getQuantity()) || dtoItem.getQuantity() == 0) {
                    HashMap<String, Object> map = new HashMap<>(4);
                    map.put("code", dtoItem.getNo());
                    map.put("errorMsg", "Quantity/出库数量[" + dtoItem.getQuantity() + "]It cannot be empty or equal to 0, please check");
                    res.setFailed(res.getFailed() + dtoList.size());
                    res.getFailedList().add(map);
                    planItemDoList.clear();
                    break;
                }
                //根据平台sku获取对应的系统sku映射
                List<PlatformProductMappingDo> productMappingDoList = platProductIdMatchMappings.get(platformProductDo.getId());
                boolean flag = true;
                if (CollUtil.isNotEmpty(productMappingDoList)) {
                    for (PlatformProductMappingDo platformProductMappingDo : productMappingDoList) {
                        ProductDo productDo = productIdMatch.get(platformProductMappingDo.getSystemProductId());
                        AllocatePlanItemDo planItemDo = new AllocatePlanItemDo();
                        planItemDo.setPlatformProductId(platformProductDo.getId());
                        planItemDo.setMsku(platformProductDo.getPlatformSku());
                        planItemDo.setFnsku(platformProductDo.getFnSku());
                        planItemDo.setAsin(platformProductDo.getAsin());
                        planItemDo.setParentAsin(platformProductDo.getParentSku());
                        planItemDo.setImageUrl(platformProductDo.getImageUrl());
                        planItemDo.setPerBoxQty(Objects.isNull(platformProductMappingDo.getQty()) ? 1 : platformProductMappingDo.getQty());
                        planItemDo.setBoxQty(dtoItem.getQuantity());
                        planItemDo.setPlanShipments(planItemDo.getPerBoxQty() * planItemDo.getBoxQty());
                        if (Objects.isNull(productDo)) {
                            planItemDoList.clear();
                            flag = false;
                            break;
                        } else {
                            planItemDo.setJanCode(productDo.getJanCode());
                            planItemDo.setProductId(productDo.getId());
                            planItemDo.setSku(productDo.getSku());
                            planItemDo.setProductName(productDo.getProductName());
                        }
                        planItemDoList.add(planItemDo);
                    }
                } else {
                    AllocatePlanItemDo planItemDo = new AllocatePlanItemDo();
                    planItemDo.setPlatformProductId(platformProductDo.getId());
                    planItemDo.setMsku(platformProductDo.getPlatformSku());
                    planItemDo.setFnsku(platformProductDo.getFnSku());
                    planItemDo.setAsin(platformProductDo.getAsin());
                    planItemDo.setParentAsin(platformProductDo.getParentSku());
                    planItemDo.setImageUrl(platformProductDo.getImageUrl());
                    planItemDoList.add(planItemDo);
                }
                if (!flag) {
                    break;
                }
            }
            if (CollUtil.isNotEmpty(planItemDoList)) {
                AllocatePlanDo planDo = new AllocatePlanDo();
                planDo.setTenantId(tenantId);
                planDo.setCompanyId(companyId);
                planDo.setPlanNo(allocatePlanService.generatePlanBatchNo());
                planDo.setDestinationType(1);
                planDo.setAddressBookId(dto.getFbaAddressBookId());
                planDo.setFbaShopId(storeDo.getId());
                planDo.setFbaShopName(storeDo.getStoreName());
                planDo.setDeliveryWhId(whCodeMatch.get(dto.getDeliveryWarehouseCode()).getId());
                if (StrUtil.isNotBlank(dto.getShippingServiceCode())) {
                    planDo.setLogisticsChannelId(channelCodeMatch.get(dto.getShippingServiceCode()).getId());
                }
                planDo.setTrackingNumber(dto.getTrackingNumber());
                planDo.setTotalPlanShipments(planItemDoList.stream().map(AllocatePlanItemDo::getPlanShipments).reduce(0, Integer::sum));
                planDo.setTotalPlanBox(dtoList.stream().map(AllocateOutImportByFbaDto::getQuantity).reduce(0, Integer::sum));
                planDo.setReferenceNumber(dto.getReferenceNumber());
                planDo.setRemark(dto.getRemark());
                planDo.setExpectedArrivalDate(dto.getExpectedArrivalDate());
                planDo.setExpectedArrivalTime(dto.getExpectedArrivalTime());
                planDo.setPlanShipmentTime(dto.getPlanShipmentTime());
                allocatePlanService.save(planDo);
                planItemDoList.forEach(planItemDo -> planItemDo.setOrderId(planDo.getId()));
                allocatePlanItemService.saveBatch(planItemDoList);
                res.setSuccess(res.getSuccess() + dtoList.size());
                operateRecordService.operateAddRecordAndValue(
                        SysConstant.Allocate_Plan_Import,
                        "",
                        planDo.getId(),
                        userId,
                        username,
                        OperateTypeEnum.AllocatePlan
                );
            }
        }
        return res;
    }

    @Override
    public CommonBatchOperationVo import3rdWhAllocateOutbound(MultipartFile file, Long tenantId, Long userId, String username, Long companyId) {
        CommonBatchOperationVo res = new CommonBatchOperationVo();
        List<AllocateOutImportBy3rdWhDto> importDtoList;
        try {
            importDtoList = ExcelUtils.importExcel(file, 1, 0, AllocateOutImportBy3rdWhDto.class);
        } catch (Exception e) {
            throw new BizException(SysConstant.Excel_Template_Error);
        }
        Set<Integer> exceptionNoSet = importDtoList.stream().filter(
                i -> Objects.isNull(i.getNo())
                     || StrUtil.isBlank(i.getDeliveryWarehouseCode())
                     || Objects.isNull(i.getAddressBookId())
                     || StrUtil.isBlank(i.getSku())
                     || Objects.isNull(i.getPerBoxQty())
                     || Objects.isNull(i.getTotalBoxQty())
        ).map(AllocateOutImportBy3rdWhDto::getNo).collect(Collectors.toSet());
        int oldSize = importDtoList.size();
        res.setTotal(importDtoList.size());
        importDtoList = importDtoList.stream().filter(i -> !exceptionNoSet.contains(i.getNo())).collect(Collectors.toList());
        int newSize = importDtoList.size();
        if (oldSize != newSize) {
            HashMap<String, Object> map = new HashMap<>(4);
            map.put("code", StrUtil.join(",", exceptionNoSet));
            map.put("errorMsg", StringsUtil.createI18Message(SysConstant.Import_Required_Field_Blank));
            res.setFailed(res.getFailed() + oldSize - newSize);
            res.getFailedList().add(map);
            return res;
        }
        if (importDtoList.isEmpty()) {
            return res;
        }
        Map<String, WarehouseDo> whCodeMatch = warehouseService.lambdaQuery()
                .eq(WarehouseDo::getTenantId, tenantId)
                .list().stream().collect(Collectors.toMap(WarehouseDo::getWhCode, val -> val));
        List<Long> addressBookIdList = addressBookService.listObjs(
                Wrappers.<AddressBookDo>lambdaQuery()
                        .select(AddressBookDo::getId)
                        .eq(AddressBookDo::getTenantId, tenantId)
                        .eq(AddressBookDo::getCompanyId, companyId)
                        .eq(AddressBookDo::getAddressType, AddressBookTypeEnum.zero.getValue()),
                obj -> ((BigInteger) obj).longValue()
        );
        Map<String, LogisticsChannelDo> channelCodeMatch = logisticsChannelService.lambdaQuery().eq(LogisticsChannelDo::getTenantId, tenantId).list().stream().collect(Collectors.toMap(LogisticsChannelDo::getChannelCode, val -> val, (k1, k2) -> k1));
        Map<String, ProductDo> skuMatch = productService.lambdaQuery()
                .in(ProductDo::getSku, importDtoList.stream().map(AllocateOutImportBy3rdWhDto::getSku).collect(Collectors.toList())).eq(ProductDo::getTenantId, tenantId).eq(ProductDo::getCompanyId, companyId)
                .list().stream().collect(Collectors.toMap(ProductDo::getSku, val -> val));
        Map<Integer, List<AllocateOutImportBy3rdWhDto>> dtoListGroup = importDtoList.stream().collect(Collectors.groupingBy(AllocateOutImportBy3rdWhDto::getNo));
        for (Map.Entry<Integer, List<AllocateOutImportBy3rdWhDto>> entry : dtoListGroup.entrySet()) {
            List<AllocateOutImportBy3rdWhDto> dtoList = entry.getValue();
            AllocateOutImportBy3rdWhDto dto = dtoList.get(0);
            if (whCodeMatch.get(dto.getDeliveryWarehouseCode()) == null) {
                HashMap<String, Object> map = new HashMap<>(4);
                map.put("code", dto.getNo());
                map.put("errorMsg", "Delivery Warehouse Code/发货仓库代码[" + dto.getDeliveryWarehouseCode() + "] Wrong,Please check!");
                res.setFailed(res.getFailed() + dtoList.size());
                res.getFailedList().add(map);
                continue;
            }
            if (!addressBookIdList.contains(dto.getAddressBookId())) {
                HashMap<String, Object> map = new HashMap<>(4);
                map.put("code", dto.getNo());
                map.put("errorMsg", "Destination Address/ 目的地编码[" + dto.getAddressBookId() + "] Wrong,Please check!");
                res.setFailed(res.getFailed() + dtoList.size());
                res.getFailedList().add(map);
                continue;
            }
            if (StrUtil.isNotBlank(dto.getDestinationWarehouseCode())) {
                if (whCodeMatch.get(dto.getDestinationWarehouseCode()) == null) {
                    HashMap<String, Object> map = new HashMap<>(4);
                    map.put("code", dto.getNo());
                    map.put("errorMsg", "Destination Warehouse Code/目标仓库代码[" + dto.getDestinationWarehouseCode() + "] Wrong,Please check!");
                    res.setFailed(res.getFailed() + dtoList.size());
                    res.getFailedList().add(map);
                    continue;
                }
            }
            if (StrUtil.isNotBlank(dto.getShippingServiceCode())) {
                LogisticsChannelDo logisticsChannelDo = channelCodeMatch.get(dto.getShippingServiceCode());
                if (logisticsChannelDo == null) {
                    HashMap<String, Object> map = new HashMap<>(4);
                    map.put("code", dto.getNo());
                    map.put("errorMsg", "Shipping Service Code/物流渠道代码[" + dto.getShippingServiceCode() + "] Wrong,Please check!");
                    res.setFailed(res.getFailed() + dtoList.size());
                    res.getFailedList().add(map);
                    continue;
                }
            }
            List<AllocatePlanItemDo> planItemDoList = new ArrayList<>();
            for (AllocateOutImportBy3rdWhDto dtoItem : dtoList) {
                ProductDo productDo = skuMatch.get(dtoItem.getSku());
                if (productDo == null) {
                    HashMap<String, Object> map = new HashMap<>(4);
                    map.put("code", dto.getNo());
                    map.put("errorMsg", "Sku/sku[" + dtoItem.getSku() + "] Not Found,Please check!");
                    res.setFailed(res.getFailed() + dtoList.size());
                    res.getFailedList().add(map);
                    planItemDoList.clear();
                    break;
                }
                if (Objects.isNull(dtoItem.getPerBoxQty()) || dtoItem.getPerBoxQty() == 0) {
                    HashMap<String, Object> map = new HashMap<>(4);
                    map.put("code", dtoItem.getNo());
                    map.put("errorMsg", "Quantity per box/单箱数量[" + dtoItem.getPerBoxQty() + "]It cannot be empty or equal to 0, please check");
                    res.setFailed(res.getFailed() + dtoList.size());
                    res.getFailedList().add(map);
                    planItemDoList.clear();
                    break;
                }
                if (Objects.isNull(dtoItem.getTotalBoxQty()) || dtoItem.getTotalBoxQty() == 0) {
                    HashMap<String, Object> map = new HashMap<>(4);
                    map.put("code", dtoItem.getNo());
                    map.put("errorMsg", "Total number of boxes/总箱数[" + dtoItem.getTotalBoxQty() + "]It cannot be empty or equal to 0, please check");
                    res.setFailed(res.getFailed() + dtoList.size());
                    res.getFailedList().add(map);
                    planItemDoList.clear();
                    break;
                }
                AllocatePlanItemDo planItemDo = new AllocatePlanItemDo();
                planItemDo.setProductId(productDo.getId());
                planItemDo.setSku(productDo.getSku());
                planItemDo.setProductName(productDo.getProductName());
                planItemDo.setJanCode(productDo.getJanCode());
                planItemDo.setPerBoxQty(dtoItem.getPerBoxQty());
                planItemDo.setBoxQty(dtoItem.getTotalBoxQty());
                planItemDo.setPlanShipments(planItemDo.getPerBoxQty() * planItemDo.getBoxQty());
                planItemDoList.add(planItemDo);
            }
            if (CollUtil.isNotEmpty(planItemDoList)) {
                AllocatePlanDo planDo = new AllocatePlanDo();
                planDo.setTenantId(tenantId);
                planDo.setCompanyId(companyId);
                planDo.setPlanNo(allocatePlanService.generatePlanBatchNo());
                planDo.setDestinationType(2);
                planDo.setAddressBookId(dto.getAddressBookId());
                planDo.setDeliveryWhId(whCodeMatch.get(dto.getDeliveryWarehouseCode()).getId());
                if (StrUtil.isNotBlank(dto.getDestinationWarehouseCode())) {
                    planDo.setDestinationWhId(whCodeMatch.get(dto.getDestinationWarehouseCode()).getId());
                }
                if (StrUtil.isNotBlank(dto.getShippingServiceCode())) {
                    planDo.setLogisticsChannelId(channelCodeMatch.get(dto.getShippingServiceCode()).getId());
                }
                planDo.setTrackingNumber(dto.getTrackingNumber());
                planDo.setTotalPlanShipments(planItemDoList.stream().map(AllocatePlanItemDo::getPlanShipments).reduce(0, Integer::sum));
                planDo.setTotalPlanBox(dtoList.stream().map(AllocateOutImportBy3rdWhDto::getTotalBoxQty).reduce(0, Integer::sum));
                planDo.setReferenceNumber(dto.getReferenceNumber());
                planDo.setRemark(dto.getRemark());
                planDo.setExpectedArrivalDate(dto.getExpectedArrivalDate());
                planDo.setExpectedArrivalTime(dto.getExpectedArrivalTime());
                planDo.setPlanShipmentTime(dto.getPlanShipmentTime());
                allocatePlanService.save(planDo);
                planItemDoList.forEach(planItemDo -> planItemDo.setOrderId(planDo.getId()));
                allocatePlanItemService.saveBatch(planItemDoList);
                res.setSuccess(res.getSuccess() + dtoList.size());
                operateRecordService.operateAddRecordAndValue(
                        SysConstant.Allocate_Plan_Import,
                        "",
                        planDo.getId(),
                        userId,
                        username,
                        OperateTypeEnum.AllocatePlan
                );
            }
        }
        return res;
    }

    @Override
    public CommonBatchOperationVo importTransferOutbound1(MultipartFile file, Long tenantId, Long userId, String username, Long companyId) {
        CommonBatchOperationVo res = new CommonBatchOperationVo();
        List<TransferOutImportByColDto> importDtoList;
        try {
            importDtoList = ExcelUtils.importExcel(file, 1, 0, TransferOutImportByColDto.class);
        } catch (Exception e) {
            throw new BizException(SysConstant.Excel_Template_Error);
        }
        Set<Integer> exceptionNoSet = importDtoList.stream().filter(
                i -> Objects.isNull(i.getNo())
                     || StrUtil.isBlank(i.getWarehouseCode())
                     || StrUtil.isBlank(i.getDestinationType())
                     || StrUtil.isBlank(i.getOutboundMode())
                     || StrUtil.isBlank(i.getShippingServiceCode())
                     || StrUtil.isBlank(i.getReceiver())
                     || StrUtil.isBlank(i.getCountryRegionCode())
//                     || StrUtil.isBlank(i.getCityName())
                     || StrUtil.isBlank(i.getPostCode())
                     || StrUtil.isBlank(i.getAddress1())
                     || StrUtil.isBlank(i.getSku1())
                     || Objects.isNull(i.getQuantity1())
        ).map(TransferOutImportByColDto::getNo).collect(Collectors.toSet());
        int oldSize = importDtoList.size();
        res.setTotal(importDtoList.size());
        importDtoList = importDtoList.stream()
                .filter(i -> !exceptionNoSet.contains(i.getNo()))
                .collect(Collectors.toList());
        int newSize = importDtoList.size();
        if (oldSize != newSize) {
            HashMap<String, Object> map = new HashMap<>(4);
            map.put("code", StrUtil.join(",", exceptionNoSet));
            map.put("errorMsg", StringsUtil.createI18Message(SysConstant.Import_Required_Field_Blank));
            res.setFailed(res.getFailed() + oldSize - newSize);
            res.getFailedList().add(map);
        }
        if (importDtoList.isEmpty()) {
            return res;
        }
        Map<String, WarehouseDo> whCodeMatch = warehouseService.lambdaQuery()
                .eq(WarehouseDo::getTenantId, tenantId)
                .list().stream().collect(Collectors.toMap(WarehouseDo::getWhCode, val -> val));
        Map<String, ProductDo> companyIdSkuMatch = productService.lambdaQuery()
                .eq(ProductDo::getTenantId, tenantId)
                .list().stream().collect(Collectors.toMap(key -> key.getCompanyId() + key.getSku(), val -> val));
        Map<String, LogisticsChannelDo> channelCodeMatch = logisticsChannelService.lambdaQuery().eq(LogisticsChannelDo::getTenantId, tenantId).list().stream().collect(Collectors.toMap(LogisticsChannelDo::getChannelCode, val -> val, (k1, k2) -> k1));
        List<Map<String, String>> carrierList = StaticDict.Lc_Carrier.getCarrierList();
        Set<String> boxTypeNoList = importDtoList.stream()
                .filter(item -> "按箱出库".equals(item.getOutboundMode()))
                .map(TransferOutImportByColDto::getSku1)
                .filter(StrUtil::isNotBlank)
                .collect(Collectors.toSet());
        importDtoList.stream()
                .filter(item -> "按箱出库".equals(item.getOutboundMode()))
                .map(TransferOutImportByColDto::getSku2)
                .filter(StrUtil::isNotBlank)
                .forEach(boxTypeNoList::add);
        importDtoList.stream()
                .filter(item -> "按箱出库".equals(item.getOutboundMode()))
                .map(TransferOutImportByColDto::getSku3)
                .filter(StrUtil::isNotBlank)
                .forEach(boxTypeNoList::add);
        importDtoList.stream()
                .filter(item -> "按箱出库".equals(item.getOutboundMode()))
                .map(TransferOutImportByColDto::getSku4)
                .filter(StrUtil::isNotBlank)
                .forEach(boxTypeNoList::add);
        importDtoList.stream()
                .filter(item -> "按箱出库".equals(item.getOutboundMode()))
                .map(TransferOutImportByColDto::getSku5)
                .filter(StrUtil::isNotBlank)
                .forEach(boxTypeNoList::add);
        importDtoList.stream()
                .filter(item -> "按箱出库".equals(item.getOutboundMode()))
                .map(TransferOutImportByColDto::getSku6)
                .filter(StrUtil::isNotBlank)
                .forEach(boxTypeNoList::add);
        importDtoList.stream()
                .filter(item -> "按箱出库".equals(item.getOutboundMode()))
                .map(TransferOutImportByColDto::getSku7)
                .filter(StrUtil::isNotBlank)
                .forEach(boxTypeNoList::add);
        importDtoList.stream()
                .filter(item -> "按箱出库".equals(item.getOutboundMode()))
                .map(TransferOutImportByColDto::getSku8)
                .filter(StrUtil::isNotBlank)
                .forEach(boxTypeNoList::add);
        importDtoList.stream()
                .filter(item -> "按箱出库".equals(item.getOutboundMode()))
                .map(TransferOutImportByColDto::getSku9)
                .filter(StrUtil::isNotBlank)
                .forEach(boxTypeNoList::add);
        importDtoList.stream()
                .filter(item -> "按箱出库".equals(item.getOutboundMode()))
                .map(TransferOutImportByColDto::getSku10)
                .filter(StrUtil::isNotBlank)
                .forEach(boxTypeNoList::add);
        Map<String, List<OrderBoxProductDo>> boxTypeNoMatch = new HashMap<>();
        if (CollUtil.isNotEmpty(boxTypeNoList)) {
            List<OrderBoxProductDo> bpList = boxProductService.lambdaQuery()
                    .in(OrderBoxProductDo::getBoxTypeNumber, boxTypeNoList)
                    .list();
            if (CollUtil.isNotEmpty(bpList)) {
                boxTypeNoMatch = bpList.stream().collect(Collectors.groupingBy(OrderBoxProductDo::getBoxTypeNumber));
            }
        }
        for (TransferOutImportByColDto dto : importDtoList) {
            if (whCodeMatch.get(dto.getWarehouseCode()) == null) {
                HashMap<String, Object> map = new HashMap<>(4);
                map.put("code", dto.getNo());
                map.put("errorMsg", "Warehouse Code/仓库代码[" + dto.getWarehouseCode() + "] Not Found,Please check!");
                res.setFailed(res.getFailed() + 1);
                res.getFailedList().add(map);
                continue;
            }
            if (DestinationTypeEnum.getValByDesc(dto.getDestinationType()) == null) {
                HashMap<String, Object> map = new HashMap<>(4);
                map.put("code", dto.getNo());
                map.put("errorMsg", "Destination type/目的地类型[" + dto.getDestinationType() + "] Not Found,Please check!");
                res.setFailed(res.getFailed() + 1);
                res.getFailedList().add(map);
                continue;
            }
            if (!"按产品出库".equals(dto.getOutboundMode()) && !"按箱出库".equals(dto.getOutboundMode())) {
                HashMap<String, Object> map = new HashMap<>(4);
                map.put("code", dto.getNo());
                map.put("errorMsg", "Outbound Mode/出库方式[" + dto.getOutboundMode() + "] Not Found,Please check!");
                res.setFailed(res.getFailed() + 1);
                res.getFailedList().add(map);
                continue;
            }
            Integer outboundMode = "按产品出库".equals(dto.getOutboundMode()) ? 1 : 2;
            LogisticsChannelDo logisticsChannelDo = channelCodeMatch.get(dto.getShippingServiceCode());
            if (logisticsChannelDo == null) {
                HashMap<String, Object> map = new HashMap<>(4);
                map.put("code", dto.getNo());
                map.put("errorMsg", "Shipping Service Code/物流渠道代码[" + dto.getShippingServiceCode() + "] Not Found,Please check!");
                res.setFailed(res.getFailed() + 1);
                res.getFailedList().add(map);
                continue;
            }
            if (StrUtil.isNotBlank(dto.getCarrier()) && carrierList.stream().noneMatch(i -> i.get("code").equals(dto.getCarrier()))) {
                HashMap<String, Object> map = new HashMap<>(4);
                map.put("code", dto.getNo());
                map.put("errorMsg", "Carrier/承运商[" + dto.getCarrier() + "] Not Found,Please check!");
                res.setFailed(res.getFailed() + 1);
                res.getFailedList().add(map);
                continue;
            }
            if (!CountryEnum.getCountryList().contains(dto.getCountryRegionCode())) {
                HashMap<String, Object> map = new HashMap<>(4);
                map.put("code", dto.getNo());
                map.put("errorMsg", "Recipient Country/国家/地区[" + dto.getCountryRegionCode() + "] Not Found,Please check!");
                res.setFailed(res.getFailed() + 1);
                res.getFailedList().add(map);
                continue;
            }
            StringBuilder skuDetailBuilder = new StringBuilder();
            List<OrderOutboundProductDo> outboundProductDoList = new ArrayList<>();
            int totalNum = 0;
            if (outboundMode.equals(1) ? companyIdSkuMatch.get(companyId + dto.getSku1()) == null : CollUtil.isEmpty(boxTypeNoMatch.get(dto.getSku1()))) {
                HashMap<String, Object> map = new HashMap<>(4);
                map.put("code", dto.getNo());
                map.put("errorMsg", "SKU1 or Box Type Number1/SKU1 or 箱类型号1[" + dto.getSku1() + "] Not Found,Please check!");
                res.setFailed(res.getFailed() + 1);
                res.getFailedList().add(map);
                continue;
            } else {
                if (Objects.isNull(dto.getQuantity1()) || dto.getQuantity1() == 0) {
                    HashMap<String, Object> map = new HashMap<>(4);
                    map.put("code", dto.getNo());
                    map.put("errorMsg", "Quantity1/出库数量1[" + dto.getQuantity1() + "]It cannot be empty or equal to 0, please check");
                    res.setFailed(res.getFailed() + 1);
                    res.getFailedList().add(map);
                    continue;
                } else {
                    totalNum += dto.getQuantity1();
                    if (outboundMode.equals(1)) {
                        //按产品出库
                        OrderOutboundProductDo outboundProductDo = new OrderOutboundProductDo();
                        outboundProductDo.setProductId(companyIdSkuMatch.get(companyId + dto.getSku1()).getId());
                        outboundProductDo.setForecastQty(dto.getQuantity1());
                        outboundProductDoList.add(outboundProductDo);
                        skuDetailBuilder.append(dto.getSku1()).append("*").append(dto.getQuantity1());
                    } else {
                        //按箱出库
                        List<OrderBoxProductDo> boxProductDoList = boxTypeNoMatch.get(dto.getSku1());
                        for (OrderBoxProductDo boxProductDo : boxProductDoList) {
                            OrderOutboundProductDo outboundProductDo = new OrderOutboundProductDo();
                            outboundProductDo.setProductId(boxProductDo.getProductId());
                            outboundProductDo.setBoxTypeNumber(boxProductDo.getBoxTypeNumber());
                            outboundProductDo.setForecastQty(boxProductDo.getPerBoxQty() * dto.getQuantity1());
                            outboundProductDo.setForecastBoxQty(dto.getQuantity1());
                            outboundProductDoList.add(outboundProductDo);
                        }
                        skuDetailBuilder.append(dto.getSku1()).append("*").append(dto.getQuantity1());
                    }
                }
            }
            if (StrUtil.isNotBlank(dto.getSku2()) && (outboundMode.equals(1) ? companyIdSkuMatch.get(companyId + dto.getSku2()) == null : CollUtil.isEmpty(boxTypeNoMatch.get(dto.getSku2())))) {
                HashMap<String, Object> map = new HashMap<>(4);
                map.put("code", dto.getNo());
                map.put("errorMsg", "SKU2 or Box Type Number2/SKU2 or 箱类型号2[" + dto.getSku2() + "] Not Found,Please check!");
                res.setFailed(res.getFailed() + 1);
                res.getFailedList().add(map);
                continue;
            } else if (StrUtil.isNotBlank(dto.getSku2())) {
                if (Objects.isNull(dto.getQuantity2()) || dto.getQuantity2() == 0) {
                    HashMap<String, Object> map = new HashMap<>(4);
                    map.put("code", dto.getNo());
                    map.put("errorMsg", "Quantity2/出库数量2[" + dto.getQuantity2() + "]It cannot be empty or equal to 0, please check");
                    res.setFailed(res.getFailed() + 1);
                    res.getFailedList().add(map);
                    continue;
                } else {
                    totalNum += dto.getQuantity2();
                    if (outboundMode.equals(1)) {
                        //按产品出库
                        OrderOutboundProductDo outboundProductDo = new OrderOutboundProductDo();
                        outboundProductDo.setProductId(companyIdSkuMatch.get(companyId + dto.getSku2()).getId());
                        outboundProductDo.setForecastQty(dto.getQuantity2());
                        outboundProductDoList.add(outboundProductDo);
                        skuDetailBuilder.append(",").append(dto.getSku2()).append("*").append(dto.getQuantity2());
                    } else {
                        //按箱出库
                        List<OrderBoxProductDo> boxProductDoList = boxTypeNoMatch.get(dto.getSku2());
                        for (OrderBoxProductDo boxProductDo : boxProductDoList) {
                            OrderOutboundProductDo outboundProductDo = new OrderOutboundProductDo();
                            outboundProductDo.setProductId(boxProductDo.getProductId());
                            outboundProductDo.setBoxTypeNumber(boxProductDo.getBoxTypeNumber());
                            outboundProductDo.setForecastQty(boxProductDo.getPerBoxQty() * dto.getQuantity2());
                            outboundProductDo.setForecastBoxQty(dto.getQuantity2());
                            outboundProductDoList.add(outboundProductDo);
                        }
                        skuDetailBuilder.append(",").append(dto.getSku2()).append("*").append(dto.getQuantity2());
                    }
                }
            }
            if (StrUtil.isNotBlank(dto.getSku3()) && (outboundMode.equals(1) ? companyIdSkuMatch.get(companyId + dto.getSku3()) == null : CollUtil.isEmpty(boxTypeNoMatch.get(dto.getSku3())))) {
                HashMap<String, Object> map = new HashMap<>(4);
                map.put("code", dto.getNo());
                map.put("errorMsg", "SKU3 or Box Type Number3/SKU3 or 箱类型号3[" + dto.getSku3() + "] Not Found,Please check!");
                res.setFailed(res.getFailed() + 1);
                res.getFailedList().add(map);
                continue;
            } else if (StrUtil.isNotBlank(dto.getSku3())) {
                if (Objects.isNull(dto.getQuantity3()) || dto.getQuantity3() == 0) {
                    HashMap<String, Object> map = new HashMap<>(4);
                    map.put("code", dto.getNo());
                    map.put("errorMsg", "Quantity3/出库数量3[" + dto.getQuantity3() + "]It cannot be empty or equal to 0, please check");
                    res.setFailed(res.getFailed() + 1);
                    res.getFailedList().add(map);
                    continue;
                } else {
                    totalNum += dto.getQuantity3();
                    if (outboundMode.equals(1)) {
                        //按产品出库
                        OrderOutboundProductDo outboundProductDo = new OrderOutboundProductDo();
                        outboundProductDo.setProductId(companyIdSkuMatch.get(companyId + dto.getSku3()).getId());
                        outboundProductDo.setForecastQty(dto.getQuantity3());
                        outboundProductDoList.add(outboundProductDo);
                        skuDetailBuilder.append(",").append(dto.getSku3()).append("*").append(dto.getQuantity3());
                    } else {
                        //按箱出库
                        List<OrderBoxProductDo> boxProductDoList = boxTypeNoMatch.get(dto.getSku3());
                        for (OrderBoxProductDo boxProductDo : boxProductDoList) {
                            OrderOutboundProductDo outboundProductDo = new OrderOutboundProductDo();
                            outboundProductDo.setProductId(boxProductDo.getProductId());
                            outboundProductDo.setBoxTypeNumber(boxProductDo.getBoxTypeNumber());
                            outboundProductDo.setForecastQty(boxProductDo.getPerBoxQty() * dto.getQuantity3());
                            outboundProductDo.setForecastBoxQty(dto.getQuantity3());
                            outboundProductDoList.add(outboundProductDo);
                        }
                        skuDetailBuilder.append(",").append(dto.getSku3()).append("*").append(dto.getQuantity3());
                    }
                }
            }
            if (StrUtil.isNotBlank(dto.getSku4()) && (outboundMode.equals(1) ? companyIdSkuMatch.get(companyId + dto.getSku4()) == null : CollUtil.isEmpty(boxTypeNoMatch.get(dto.getSku4())))) {
                HashMap<String, Object> map = new HashMap<>(4);
                map.put("code", dto.getNo());
                map.put("errorMsg", "SKU4 or Box Type Number4/SKU4 or 箱类型号4[" + dto.getSku4() + "] Not Found,Please check!");
                res.setFailed(res.getFailed() + 1);
                res.getFailedList().add(map);
                continue;
            } else if (StrUtil.isNotBlank(dto.getSku4())) {
                if (Objects.isNull(dto.getQuantity4()) || dto.getQuantity4() == 0) {
                    HashMap<String, Object> map = new HashMap<>(4);
                    map.put("code", dto.getNo());
                    map.put("errorMsg", "Quantity4/出库数量4[" + dto.getQuantity4() + "]It cannot be empty or equal to 0, please check");
                    res.setFailed(res.getFailed() + 1);
                    res.getFailedList().add(map);
                    continue;
                } else {
                    totalNum += dto.getQuantity4();
                    if (outboundMode.equals(1)) {
                        //按产品出库
                        OrderOutboundProductDo outboundProductDo = new OrderOutboundProductDo();
                        outboundProductDo.setProductId(companyIdSkuMatch.get(companyId + dto.getSku4()).getId());
                        outboundProductDo.setForecastQty(dto.getQuantity4());
                        outboundProductDoList.add(outboundProductDo);
                        skuDetailBuilder.append(",").append(dto.getSku4()).append("*").append(dto.getQuantity4());
                    } else {
                        //按箱出库
                        List<OrderBoxProductDo> boxProductDoList = boxTypeNoMatch.get(dto.getSku4());
                        for (OrderBoxProductDo boxProductDo : boxProductDoList) {
                            OrderOutboundProductDo outboundProductDo = new OrderOutboundProductDo();
                            outboundProductDo.setProductId(boxProductDo.getProductId());
                            outboundProductDo.setBoxTypeNumber(boxProductDo.getBoxTypeNumber());
                            outboundProductDo.setForecastQty(boxProductDo.getPerBoxQty() * dto.getQuantity4());
                            outboundProductDo.setForecastBoxQty(dto.getQuantity4());
                            outboundProductDoList.add(outboundProductDo);
                        }
                        skuDetailBuilder.append(",").append(dto.getSku4()).append("*").append(dto.getQuantity4());
                    }
                }
            }
            if (StrUtil.isNotBlank(dto.getSku5()) && (outboundMode.equals(1) ? companyIdSkuMatch.get(companyId + dto.getSku4()) == null : CollUtil.isEmpty(boxTypeNoMatch.get(dto.getSku5())))) {
                HashMap<String, Object> map = new HashMap<>(4);
                map.put("code", dto.getNo());
                map.put("errorMsg", "SKU5 or Box Type Number5/SKU5 or 箱类型号5[" + dto.getSku5() + "] Not Found,Please check!");
                res.setFailed(res.getFailed() + 1);
                res.getFailedList().add(map);
                continue;
            } else if (StrUtil.isNotBlank(dto.getSku5())) {
                if (Objects.isNull(dto.getQuantity5()) || dto.getQuantity5() == 0) {
                    HashMap<String, Object> map = new HashMap<>(4);
                    map.put("code", dto.getNo());
                    map.put("errorMsg", "Quantity5/出库数量5[" + dto.getQuantity5() + "]It cannot be empty or equal to 0, please check");
                    res.setFailed(res.getFailed() + 1);
                    res.getFailedList().add(map);
                    continue;
                } else {
                    totalNum += dto.getQuantity5();
                    if (outboundMode.equals(1)) {
                        //按产品出库
                        OrderOutboundProductDo outboundProductDo = new OrderOutboundProductDo();
                        outboundProductDo.setProductId(companyIdSkuMatch.get(companyId + dto.getSku5()).getId());
                        outboundProductDo.setForecastQty(dto.getQuantity5());
                        outboundProductDoList.add(outboundProductDo);
                        skuDetailBuilder.append(",").append(dto.getSku5()).append("*").append(dto.getQuantity5());
                    } else {
                        //按箱出库
                        List<OrderBoxProductDo> boxProductDoList = boxTypeNoMatch.get(dto.getSku5());
                        for (OrderBoxProductDo boxProductDo : boxProductDoList) {
                            OrderOutboundProductDo outboundProductDo = new OrderOutboundProductDo();
                            outboundProductDo.setProductId(boxProductDo.getProductId());
                            outboundProductDo.setBoxTypeNumber(boxProductDo.getBoxTypeNumber());
                            outboundProductDo.setForecastQty(boxProductDo.getPerBoxQty() * dto.getQuantity5());
                            outboundProductDo.setForecastBoxQty(dto.getQuantity5());
                            outboundProductDoList.add(outboundProductDo);
                        }
                        skuDetailBuilder.append(",").append(dto.getSku5()).append("*").append(dto.getQuantity5());
                    }
                }
            }
            if (StrUtil.isNotBlank(dto.getSku6()) && (outboundMode.equals(1) ? companyIdSkuMatch.get(companyId + dto.getSku6()) == null : CollUtil.isEmpty(boxTypeNoMatch.get(dto.getSku6())))) {
                HashMap<String, Object> map = new HashMap<>(4);
                map.put("code", dto.getNo());
                map.put("errorMsg", "SKU6 or Box Type Number6/SKU6 or 箱类型号6[" + dto.getSku6() + "] Not Found,Please check!");
                res.setFailed(res.getFailed() + 1);
                res.getFailedList().add(map);
                continue;
            } else if (StrUtil.isNotBlank(dto.getSku6())) {
                if (Objects.isNull(dto.getQuantity6()) || dto.getQuantity6() == 0) {
                    HashMap<String, Object> map = new HashMap<>(4);
                    map.put("code", dto.getNo());
                    map.put("errorMsg", "Quantity6/出库数量6[" + dto.getQuantity6() + "]It cannot be empty or equal to 0, please check");
                    res.setFailed(res.getFailed() + 1);
                    res.getFailedList().add(map);
                    continue;
                } else {
                    totalNum += dto.getQuantity6();
                    if (outboundMode.equals(1)) {
                        //按产品出库
                        OrderOutboundProductDo outboundProductDo = new OrderOutboundProductDo();
                        outboundProductDo.setProductId(companyIdSkuMatch.get(companyId + dto.getSku6()).getId());
                        outboundProductDo.setForecastQty(dto.getQuantity6());
                        outboundProductDoList.add(outboundProductDo);
                        skuDetailBuilder.append(",").append(dto.getSku6()).append("*").append(dto.getQuantity6());
                    } else {
                        //按箱出库
                        List<OrderBoxProductDo> boxProductDoList = boxTypeNoMatch.get(dto.getSku6());
                        for (OrderBoxProductDo boxProductDo : boxProductDoList) {
                            OrderOutboundProductDo outboundProductDo = new OrderOutboundProductDo();
                            outboundProductDo.setProductId(boxProductDo.getProductId());
                            outboundProductDo.setBoxTypeNumber(boxProductDo.getBoxTypeNumber());
                            outboundProductDo.setForecastQty(boxProductDo.getPerBoxQty() * dto.getQuantity6());
                            outboundProductDo.setForecastBoxQty(dto.getQuantity6());
                            outboundProductDoList.add(outboundProductDo);
                        }
                        skuDetailBuilder.append(",").append(dto.getSku6()).append("*").append(dto.getQuantity6());
                    }
                }
            }
            if (StrUtil.isNotBlank(dto.getSku7()) && (outboundMode.equals(1) ? companyIdSkuMatch.get(companyId + dto.getSku7()) == null : CollUtil.isEmpty(boxTypeNoMatch.get(dto.getSku7())))) {
                HashMap<String, Object> map = new HashMap<>(4);
                map.put("code", dto.getNo());
                map.put("errorMsg", "SKU7 or Box Type Number7/SKU7 or 箱类型号7[" + dto.getSku7() + "] Not Found,Please check!");
                res.setFailed(res.getFailed() + 1);
                res.getFailedList().add(map);
                continue;
            } else if (StrUtil.isNotBlank(dto.getSku7())) {
                if (Objects.isNull(dto.getQuantity7()) || dto.getQuantity7() == 0) {
                    HashMap<String, Object> map = new HashMap<>(4);
                    map.put("code", dto.getNo());
                    map.put("errorMsg", "Quantity7/出库数量7[" + dto.getQuantity7() + "]It cannot be empty or equal to 0, please check");
                    res.setFailed(res.getFailed() + 1);
                    res.getFailedList().add(map);
                    continue;
                } else {
                    totalNum += dto.getQuantity7();
                    if (outboundMode.equals(1)) {
                        //按产品出库
                        OrderOutboundProductDo outboundProductDo = new OrderOutboundProductDo();
                        outboundProductDo.setProductId(companyIdSkuMatch.get(companyId + dto.getSku7()).getId());
                        outboundProductDo.setForecastQty(dto.getQuantity7());
                        outboundProductDoList.add(outboundProductDo);
                        skuDetailBuilder.append(",").append(dto.getSku7()).append("*").append(dto.getQuantity7());
                    } else {
                        //按箱出库
                        List<OrderBoxProductDo> boxProductDoList = boxTypeNoMatch.get(dto.getSku7());
                        for (OrderBoxProductDo boxProductDo : boxProductDoList) {
                            OrderOutboundProductDo outboundProductDo = new OrderOutboundProductDo();
                            outboundProductDo.setProductId(boxProductDo.getProductId());
                            outboundProductDo.setBoxTypeNumber(boxProductDo.getBoxTypeNumber());
                            outboundProductDo.setForecastQty(boxProductDo.getPerBoxQty() * dto.getQuantity7());
                            outboundProductDo.setForecastBoxQty(dto.getQuantity7());
                            outboundProductDoList.add(outboundProductDo);
                        }
                        skuDetailBuilder.append(",").append(dto.getSku7()).append("*").append(dto.getQuantity7());
                    }
                }
            }
            if (StrUtil.isNotBlank(dto.getSku8()) && (outboundMode.equals(1) ? companyIdSkuMatch.get(companyId + dto.getSku8()) == null : CollUtil.isEmpty(boxTypeNoMatch.get(dto.getSku8())))) {
                HashMap<String, Object> map = new HashMap<>(4);
                map.put("code", dto.getNo());
                map.put("errorMsg", "SKU8 or Box Type Number8/SKU8 or 箱类型号8[" + dto.getSku8() + "] Not Found,Please check!");
                res.setFailed(res.getFailed() + 1);
                res.getFailedList().add(map);
                continue;
            } else if (StrUtil.isNotBlank(dto.getSku8())) {
                if (Objects.isNull(dto.getQuantity8()) || dto.getQuantity8() == 0) {
                    HashMap<String, Object> map = new HashMap<>(4);
                    map.put("code", dto.getNo());
                    map.put("errorMsg", "Quantity8/出库数量8[" + dto.getQuantity8() + "]It cannot be empty or equal to 0, please check");
                    res.setFailed(res.getFailed() + 1);
                    res.getFailedList().add(map);
                    continue;
                } else {
                    totalNum += dto.getQuantity8();
                    if (outboundMode.equals(1)) {
                        //按产品出库
                        OrderOutboundProductDo outboundProductDo = new OrderOutboundProductDo();
                        outboundProductDo.setProductId(companyIdSkuMatch.get(companyId + dto.getSku8()).getId());
                        outboundProductDo.setForecastQty(dto.getQuantity8());
                        outboundProductDoList.add(outboundProductDo);
                        skuDetailBuilder.append(",").append(dto.getSku8()).append("*").append(dto.getQuantity8());
                    } else {
                        //按箱出库
                        List<OrderBoxProductDo> boxProductDoList = boxTypeNoMatch.get(dto.getSku8());
                        for (OrderBoxProductDo boxProductDo : boxProductDoList) {
                            OrderOutboundProductDo outboundProductDo = new OrderOutboundProductDo();
                            outboundProductDo.setProductId(boxProductDo.getProductId());
                            outboundProductDo.setBoxTypeNumber(boxProductDo.getBoxTypeNumber());
                            outboundProductDo.setForecastQty(boxProductDo.getPerBoxQty() * dto.getQuantity8());
                            outboundProductDo.setForecastBoxQty(dto.getQuantity8());
                            outboundProductDoList.add(outboundProductDo);
                        }
                        skuDetailBuilder.append(",").append(dto.getSku8()).append("*").append(dto.getQuantity8());
                    }
                }
            }
            if (StrUtil.isNotBlank(dto.getSku9()) && (outboundMode.equals(1) ? companyIdSkuMatch.get(companyId + dto.getSku9()) == null : CollUtil.isEmpty(boxTypeNoMatch.get(dto.getSku9())))) {
                HashMap<String, Object> map = new HashMap<>(4);
                map.put("code", dto.getNo());
                map.put("errorMsg", "SKU9 or Box Type Number9/SKU9 or 箱类型号9[" + dto.getSku9() + "] Not Found,Please check!");
                res.setFailed(res.getFailed() + 1);
                res.getFailedList().add(map);
                continue;
            } else if (StrUtil.isNotBlank(dto.getSku9())) {
                if (Objects.isNull(dto.getQuantity9()) || dto.getQuantity9() == 0) {
                    HashMap<String, Object> map = new HashMap<>(4);
                    map.put("code", dto.getNo());
                    map.put("errorMsg", "Quantity9/出库数量9[" + dto.getQuantity9() + "]It cannot be empty or equal to 0, please check");
                    res.setFailed(res.getFailed() + 1);
                    res.getFailedList().add(map);
                    continue;
                } else {
                    totalNum += dto.getQuantity9();
                    if (outboundMode.equals(1)) {
                        //按产品出库
                        OrderOutboundProductDo outboundProductDo = new OrderOutboundProductDo();
                        outboundProductDo.setProductId(companyIdSkuMatch.get(companyId + dto.getSku9()).getId());
                        outboundProductDo.setForecastQty(dto.getQuantity9());
                        outboundProductDoList.add(outboundProductDo);
                        skuDetailBuilder.append(",").append(dto.getSku9()).append("*").append(dto.getQuantity9());
                    } else {
                        //按箱出库
                        List<OrderBoxProductDo> boxProductDoList = boxTypeNoMatch.get(dto.getSku9());
                        for (OrderBoxProductDo boxProductDo : boxProductDoList) {
                            OrderOutboundProductDo outboundProductDo = new OrderOutboundProductDo();
                            outboundProductDo.setProductId(boxProductDo.getProductId());
                            outboundProductDo.setBoxTypeNumber(boxProductDo.getBoxTypeNumber());
                            outboundProductDo.setForecastQty(boxProductDo.getPerBoxQty() * dto.getQuantity9());
                            outboundProductDo.setForecastBoxQty(dto.getQuantity9());
                            outboundProductDoList.add(outboundProductDo);
                        }
                        skuDetailBuilder.append(",").append(dto.getSku9()).append("*").append(dto.getQuantity9());
                    }
                }
            }
            if (StrUtil.isNotBlank(dto.getSku10()) && (outboundMode.equals(1) ? companyIdSkuMatch.get(companyId + dto.getSku10()) == null : CollUtil.isEmpty(boxTypeNoMatch.get(dto.getSku10())))) {
                HashMap<String, Object> map = new HashMap<>(4);
                map.put("code", dto.getNo());
                map.put("errorMsg", "SKU10 or Box Type Number10/SKU10 or 箱类型号10[" + dto.getSku10() + "] Not Found,Please check!");
                res.setFailed(res.getFailed() + 1);
                res.getFailedList().add(map);
                continue;
            } else if (StrUtil.isNotBlank(dto.getSku10())) {
                if (Objects.isNull(dto.getQuantity10()) || dto.getQuantity10() == 0) {
                    HashMap<String, Object> map = new HashMap<>(4);
                    map.put("code", dto.getNo());
                    map.put("errorMsg", "Quantity10/出库数量10[" + dto.getQuantity10() + "]It cannot be empty or equal to 0, please check");
                    res.setFailed(res.getFailed() + 1);
                    res.getFailedList().add(map);
                    continue;
                } else {
                    totalNum += dto.getQuantity10();
                    if (outboundMode.equals(1)) {
                        //按产品出库
                        OrderOutboundProductDo outboundProductDo = new OrderOutboundProductDo();
                        outboundProductDo.setProductId(companyIdSkuMatch.get(companyId + dto.getSku10()).getId());
                        outboundProductDo.setForecastQty(dto.getQuantity10());
                        outboundProductDoList.add(outboundProductDo);
                        skuDetailBuilder.append(",").append(dto.getSku10()).append("*").append(dto.getQuantity10());
                    } else {
                        //按箱出库
                        List<OrderBoxProductDo> boxProductDoList = boxTypeNoMatch.get(dto.getSku10());
                        for (OrderBoxProductDo boxProductDo : boxProductDoList) {
                            OrderOutboundProductDo outboundProductDo = new OrderOutboundProductDo();
                            outboundProductDo.setProductId(boxProductDo.getProductId());
                            outboundProductDo.setBoxTypeNumber(boxProductDo.getBoxTypeNumber());
                            outboundProductDo.setForecastQty(boxProductDo.getPerBoxQty() * dto.getQuantity10());
                            outboundProductDo.setForecastBoxQty(dto.getQuantity10());
                            outboundProductDoList.add(outboundProductDo);
                        }
                        skuDetailBuilder.append(",").append(dto.getSku10()).append("*").append(dto.getQuantity10());
                    }
                }
            }
            this.importTransferOutboundSave(
                    dto,
                    null,
                    skuDetailBuilder.toString(),
                    outboundMode,
                    totalNum,
                    whCodeMatch,
                    outboundProductDoList,
                    logisticsChannelDo,
                    userId,
                    username
            );
            res.setSuccess(res.getSuccess() + 1);
        }
        return res;
    }

    @Override
    public CommonBatchOperationVo importTransferOutbound2(MultipartFile file, Long tenantId, Long userId, String username, Long companyId) {
        CommonBatchOperationVo res = new CommonBatchOperationVo();
        List<TransferOutImportByRowDto> importDtoList;
        try {
            importDtoList = ExcelUtils.importExcel(file, 1, 0, TransferOutImportByRowDto.class);
        } catch (Exception e) {
            throw new BizException(SysConstant.Excel_Template_Error);
        }
        Set<Integer> exceptionNoSet = importDtoList.stream().filter(
                i -> Objects.isNull(i.getNo())
                     || StrUtil.isBlank(i.getWarehouseCode())
                     || StrUtil.isBlank(i.getDestinationType())
                     || StrUtil.isBlank(i.getOutboundMode())
                     || StrUtil.isBlank(i.getShippingServiceCode())
                     || StrUtil.isBlank(i.getReceiver())
                     || StrUtil.isBlank(i.getCountryRegionCode())
//                     || StrUtil.isBlank(i.getCityName())
                     || StrUtil.isBlank(i.getPostCode())
                     || StrUtil.isBlank(i.getAddress1())
                     || StrUtil.isBlank(i.getSku())
                     || Objects.isNull(i.getQuantity())
        ).map(TransferOutImportByRowDto::getNo).collect(Collectors.toSet());
        int oldSize = importDtoList.size();
        res.setTotal(importDtoList.size());
        importDtoList = importDtoList.stream()
                .filter(i -> !exceptionNoSet.contains(i.getNo()))
                .collect(Collectors.toList());
        int newSize = importDtoList.size();
        if (oldSize != newSize) {
            HashMap<String, Object> map = new HashMap<>(4);
            map.put("code", StrUtil.join(",", exceptionNoSet));
            map.put("errorMsg", StringsUtil.createI18Message(SysConstant.Import_Required_Field_Blank));
            res.setFailed(res.getFailed() + oldSize - newSize);
            res.getFailedList().add(map);
        }
        if (importDtoList.isEmpty()) {
            return res;
        }
        Map<String, WarehouseDo> whCodeMatch = warehouseService.lambdaQuery()
                .eq(WarehouseDo::getTenantId, tenantId)
                .list().stream().collect(Collectors.toMap(WarehouseDo::getWhCode, val -> val));
        Map<String, ProductDo> companyIdSkuMatch = productService.lambdaQuery()
                .eq(ProductDo::getTenantId, tenantId)
                .list().stream().collect(Collectors.toMap(key -> key.getCompanyId() + key.getSku(), val -> val));
        Map<String, LogisticsChannelDo> channelCodeMatch = logisticsChannelService.lambdaQuery().eq(LogisticsChannelDo::getTenantId, tenantId).list().stream().collect(Collectors.toMap(LogisticsChannelDo::getChannelCode, val -> val, (k1, k2) -> k1));
        List<Map<String, String>> carrierList = StaticDict.Lc_Carrier.getCarrierList();
        Set<String> boxTypeNoList = importDtoList.stream()
                .filter(item -> "按箱出库".equals(item.getOutboundMode()))
                .map(TransferOutImportByRowDto::getSku)
                .filter(StrUtil::isNotBlank)
                .collect(Collectors.toSet());
        Map<String, List<OrderBoxProductDo>> boxTypeNoMatch = new HashMap<>();
        if (CollUtil.isNotEmpty(boxTypeNoList)) {
            List<OrderBoxProductDo> bpList = boxProductService.lambdaQuery()
                    .in(OrderBoxProductDo::getBoxTypeNumber, boxTypeNoList)
                    .list();
            if (CollUtil.isNotEmpty(bpList)) {
                boxTypeNoMatch = bpList.stream().collect(Collectors.groupingBy(OrderBoxProductDo::getBoxTypeNumber));
            }
        }
        Map<Integer, List<TransferOutImportByRowDto>> dtoListGroup = importDtoList.stream().collect(Collectors.groupingBy(TransferOutImportByRowDto::getNo));
        for (Map.Entry<Integer, List<TransferOutImportByRowDto>> entry : dtoListGroup.entrySet()) {
            List<TransferOutImportByRowDto> dtoList = entry.getValue();
            TransferOutImportByRowDto dto = dtoList.get(0);
            if (whCodeMatch.get(dto.getWarehouseCode()) == null) {
                HashMap<String, Object> map = new HashMap<>(4);
                map.put("code", dto.getNo());
                map.put("errorMsg", "Warehouse Code/仓库代码[" + dto.getWarehouseCode() + "] Not Found,Please check");
                res.setFailed(res.getFailed() + dtoList.size());
                res.getFailedList().add(map);
                continue;
            }
            if (DestinationTypeEnum.getValByDesc(dto.getDestinationType()) == null) {
                HashMap<String, Object> map = new HashMap<>(4);
                map.put("code", dto.getNo());
                map.put("errorMsg", "Destination type/目的地类型[" + dto.getDestinationType() + "] Not Found,Please check");
                res.setFailed(res.getFailed() + dtoList.size());
                res.getFailedList().add(map);
                continue;
            }
            if (!"按产品出库".equals(dto.getOutboundMode()) && !"按箱出库".equals(dto.getOutboundMode())) {
                HashMap<String, Object> map = new HashMap<>(4);
                map.put("code", dto.getNo());
                map.put("errorMsg", "Outbound Mode/出库方式[" + dto.getOutboundMode() + "] Not Found,Please check");
                res.setFailed(res.getFailed() + dtoList.size());
                res.getFailedList().add(map);
                continue;
            }
            Integer outboundMode = "按产品出库".equals(dto.getOutboundMode()) ? 1 : 2;
            LogisticsChannelDo logisticsChannelDo = channelCodeMatch.get(dto.getShippingServiceCode());
            if (logisticsChannelDo == null) {
                HashMap<String, Object> map = new HashMap<>(4);
                map.put("code", dto.getNo());
                map.put("errorMsg", "Shipping Service Code/物流渠道代码[" + dto.getShippingServiceCode() + "] Not Found,Please check");
                res.setFailed(res.getFailed() + dtoList.size());
                res.getFailedList().add(map);
                continue;
            }
            if (StrUtil.isNotBlank(dto.getCarrier()) && carrierList.stream().noneMatch(i -> i.get("code").equals(dto.getCarrier()))) {
                HashMap<String, Object> map = new HashMap<>(4);
                map.put("code", dto.getNo());
                map.put("errorMsg", "Carrier/承运商[" + dto.getCarrier() + "] Not Found,Please check");
                res.setFailed(res.getFailed() + dtoList.size());
                res.getFailedList().add(map);
                continue;
            }
            if (!CountryEnum.getCountryList().contains(dto.getCountryRegionCode())) {
                HashMap<String, Object> map = new HashMap<>(4);
                map.put("code", dto.getNo());
                map.put("errorMsg", "Recipient Country/国家/地区[" + dto.getCountryRegionCode() + "] Wrong,Please check,");
                res.setFailed(res.getFailed() + dtoList.size());
                res.getFailedList().add(map);
                continue;
            }
            StringBuilder skuDetailBuilder = new StringBuilder();
            List<OrderOutboundProductDo> outboundProductDoList = new ArrayList<>();
            int totalNum = 0;
            boolean skuAllExist = true;
            for (TransferOutImportByRowDto rowDto : dtoList) {
                if (outboundMode.equals(1) ? companyIdSkuMatch.get(companyId + rowDto.getSku()) == null : CollUtil.isEmpty(boxTypeNoMatch.get(rowDto.getSku()))) {
                    HashMap<String, Object> map = new HashMap<>(4);
                    map.put("code", dto.getNo());
                    map.put("errorMsg", "SKU or Box Type Number/SKU or 箱类型号[" + rowDto.getSku() + "] Not Found,Please check Sku");
                    res.setFailed(res.getFailed() + dtoList.size());
                    res.getFailedList().add(map);
                    skuAllExist = false;
                    break;
                } else {
                    if (Objects.isNull(rowDto.getQuantity()) || rowDto.getQuantity() == 0) {
                        HashMap<String, Object> map = new HashMap<>(4);
                        map.put("code", dto.getNo());
                        map.put("errorMsg", "Quantity/出库数量 It cannot be empty or equal to 0, please check");
                        res.setFailed(res.getFailed() + dtoList.size());
                        res.getFailedList().add(map);
                        skuAllExist = false;
                        break;
                    } else {
                        totalNum += rowDto.getQuantity();
                        if (outboundMode.equals(1)) {
                            //按产品出库
                            OrderOutboundProductDo outboundProductDo = new OrderOutboundProductDo();
                            outboundProductDo.setProductId(companyIdSkuMatch.get(companyId + rowDto.getSku()).getId());
                            outboundProductDo.setForecastQty(rowDto.getQuantity());
                            outboundProductDoList.add(outboundProductDo);
                            skuDetailBuilder.append(rowDto.getSku()).append("*").append(rowDto.getQuantity()).append(",");
                        } else {
                            //按箱出库
                            List<OrderBoxProductDo> boxProductDoList = boxTypeNoMatch.get(rowDto.getSku());
                            for (OrderBoxProductDo boxProductDo : boxProductDoList) {
                                OrderOutboundProductDo outboundProductDo = new OrderOutboundProductDo();
                                outboundProductDo.setProductId(boxProductDo.getProductId());
                                outboundProductDo.setBoxTypeNumber(boxProductDo.getBoxTypeNumber());
                                outboundProductDo.setForecastQty(boxProductDo.getPerBoxQty() * rowDto.getQuantity());
                                outboundProductDo.setForecastBoxQty(rowDto.getQuantity());
                                outboundProductDoList.add(outboundProductDo);
                            }
                            skuDetailBuilder.append(rowDto.getSku()).append("*").append(rowDto.getQuantity()).append(",");
                        }
                    }
                }
            }
            if (skuAllExist) {
                this.importTransferOutboundSave(
                        null,
                        dto,
                        skuDetailBuilder.substring(0, skuDetailBuilder.length() - 1),
                        outboundMode,
                        totalNum,
                        whCodeMatch,
                        outboundProductDoList,
                        logisticsChannelDo,
                        userId,
                        username
                );
                res.setSuccess(res.getSuccess() + dtoList.size());
            }
        }
        return res;
    }

    /**
     * 导入中转出库单保存方法
     *
     * @param colDto                一行一单
     * @param rowDto                多行一单
     * @param outboundMode          出库方式
     * @param totalNum              出库产品数或箱数
     * @param whCodeMatch           仓库代码匹配实体
     * @param outboundProductDoList 出库产品数据
     * @param logisticsChannelDo    物流渠道
     * @param userId                操作人
     * @param username              操作人
     */
    public void importTransferOutboundSave(TransferOutImportByColDto colDto,
                                           TransferOutImportByRowDto rowDto,
                                           String skuDetail,
                                           Integer outboundMode,
                                           Integer totalNum,
                                           Map<String, WarehouseDo> whCodeMatch,
                                           List<OrderOutboundProductDo> outboundProductDoList,
                                           LogisticsChannelDo logisticsChannelDo,
                                           Long userId, String username) {
        OrderOutboundDo entityDo = new OrderOutboundDo();
//        BeanUtils.copyProperties(Objects.isNull(colDto) ? rowDto : colDto, entityDo);
        if (Objects.isNull(colDto)) {
            BeanConvert.INSTANCE.transferOutImportByRowDtoToOutboundDo(rowDto, entityDo);
        } else {
            BeanConvert.INSTANCE.transferOutImportByColDtoToOutboundDo(colDto, entityDo);
        }
        entityDo.setSkuDetail(skuDetail);
        entityDo.setWhId(whCodeMatch.get(Objects.isNull(colDto) ? rowDto.getWarehouseCode() : colDto.getWarehouseCode()).getId());
        entityDo.setOutboundNo(outboundService.generateOutboundNo("OBC"));
        entityDo.setDestinationType(DestinationTypeEnum.getValByDesc(Objects.isNull(colDto) ? rowDto.getDestinationType() : colDto.getDestinationType()));
        entityDo.setOutboundMode(outboundMode);
        entityDo.setLogisticsChannelId(logisticsChannelDo.getId());
        if (StrUtil.isEmpty(Objects.isNull(colDto) ? rowDto.getCarrier() : colDto.getCarrier())) {
            entityDo.setCarrier(logisticsChannelDo.getCarrierCode());
        }
        entityDo.setOrderType(OutboundTypeEnum.two.getValue());
        entityDo.setStatus(TransferOutboundStatusEnum.zero.getValue());
        entityDo.setInsuranceFlag(Boolean.FALSE);
        entityDo.setTotalNum(totalNum);
        outboundService.save(entityDo);
        //操作子表
        outboundProductDoList.forEach(orderOutboundProductDo -> {
            orderOutboundProductDo.setOutboundId(entityDo.getId());
        });
        outboundProductService.saveBatch(outboundProductDoList);
        //为计算包裹尺寸组装数据
        Map<Long, Integer> productIdMatchNum = outboundProductDoList.stream().collect(
                Collectors.toMap(
                        OrderOutboundProductDo::getProductId,
                        OrderOutboundProductDo::getForecastQty,
                        Integer::sum
                )
        );
        List<ProductDo> productDoList = productService.lambdaQuery()
                .in(ProductDo::getId, productIdMatchNum.keySet())
                .list();
        List<PackageSizeVo> packageSizeVoList = new ArrayList<>();
        productDoList.forEach(productDo ->
                packageSizeVoList.add(
                        PackageSizeVo.builder()
                                .productId(productDo.getId())
                                .sku(productDo.getSku())
                                .qty(productIdMatchNum.get(productDo.getId()))
                                .length(productDo.getWmsLength() == null ? productDo.getLength() : productDo.getWmsLength())
                                .width(productDo.getWmsWidth() == null ? productDo.getWidth() : productDo.getWmsWidth())
                                .height(productDo.getWmsHeight() == null ? productDo.getHeight() : productDo.getWmsHeight())
                                .weight(productDo.getWmsWeight() == null ? productDo.getWeight() : productDo.getWmsWeight())
                                .build()
                )
        );
        //根据产品列表计算包裹尺寸
        outboundService.calculatePackageSize(entityDo.getId(), 1, entityDo.getTrackingNumber(), entityDo.getLabelFileId(), packageSizeVoList, true);
        //新增创建批发单进度
        outboundService.createTransferProgress(entityDo, username);
        operateRecordService.operateAddRecordAndValue(
                SysConstant.Outbound_Import,
                "",
                entityDo.getId(),
                userId,
                username,
                OperateTypeEnum.OrderOutbound
        );
    }

    @Override
    public CommonBatchOperationVo importUpdateTrackingNumber(MultipartFile file, Long tenantId, Long userId, String username, Long whId) {

        List<UpdateTrackingNoImportWmsDto> importDtoList;
        try {
            importDtoList = ExcelUtils.importExcel(file, 1, 0, UpdateTrackingNoImportWmsDto.class);
        } catch (Exception e) {
            throw new BizException(SysConstant.Excel_Template_Error);
        }
        return this.dealImportUpdateTrackingNumber(importDtoList, 0, tenantId, whId, userId, username, "wms");
    }

    public CommonBatchOperationVo importUpdateTrackingNumberApi(List<UpdateTrackingNoImportWmsDto> importDtoList, int orderType, Long tenantId, Long userId, String username, Long whId) {
        if (CollUtil.isEmpty(importDtoList)) {
            return new CommonBatchOperationVo();
        }
        if (importDtoList.size() > 1000) {
            throw new BizException("每次最多1000条数据");
        }
        //交给队列去处理
        rabbitTemplate.convertAndSend(RabbitQueueConstant.Order_Outbound_ImportTrackNumber,
                JSON.toJSONString(new OrderOutboundImportTrackNumberReq(importDtoList, orderType, tenantId, whId, userId, username, "jd_api")));
        return new CommonBatchOperationVo();
//        return this.dealImportUpdateTrackingNumber(importDtoList, tenantId, whId, userId, username);
    }

    @Override
    @Transactional(propagation = Propagation.NOT_SUPPORTED)
    public CommonBatchOperationVo dealImportUpdateTrackingNumber(List<UpdateTrackingNoImportWmsDto> importDtoList, int orderType, Long tenantId, Long whId, Long userId, String username, String from) {
        CommonBatchOperationVo res = new CommonBatchOperationVo();
        int oldSize = importDtoList.size();
        res.setTotal(importDtoList.size());
        importDtoList = importDtoList.stream().filter(
                i -> StrUtil.isNotBlank(i.getOutboundNo())
                     && Objects.nonNull(i.getSerialNumber())
                     && StrUtil.isNotBlank(i.getTrackingNumber())
        ).collect(Collectors.toList());
        int newSize = importDtoList.size();
        if (oldSize != newSize) {
            HashMap<String, Object> map = new HashMap<>(4);
            map.put("code", "Exception");
            map.put("errorMsg", StringsUtil.createI18Message(SysConstant.Import_Required_Field_Blank));
            res.setFailed(res.getFailed() + oldSize - newSize);
            res.getFailedList().add(map);
        }
        if (importDtoList.isEmpty()) {
            return res;
        }
        Map<String, List<UpdateTrackingNoImportWmsDto>> dataGroupNo = importDtoList.stream()
                .collect(Collectors.groupingBy(UpdateTrackingNoImportWmsDto::getOutboundNo));
        Map<String, OrderOutboundDo> outboundNoMatchDo = outboundService.lambdaQuery()
                .in(OrderOutboundDo::getOutboundNo, dataGroupNo.keySet())
                .eq(Objects.nonNull(tenantId), OrderOutboundDo::getTenantId, tenantId)
                .eq(Objects.nonNull(whId), OrderOutboundDo::getWhId, whId)
                .list().stream().collect(Collectors.toMap(OrderOutboundDo::getOutboundNo, val -> val, (v1, v2) -> v2));
        List<OperateRecordDo> operateRecordDos = new ArrayList<>();
        List<Long> successId = new ArrayList<>();
        for (Map.Entry<String, List<UpdateTrackingNoImportWmsDto>> entry : dataGroupNo.entrySet()) {
            String outboundNo = entry.getKey();
            OrderOutboundDo outboundDo = outboundNoMatchDo.get(outboundNo);
            List<UpdateTrackingNoImportWmsDto> dtoList = entry.getValue();
            if (Objects.isNull(outboundDo)) {
                HashMap<String, Object> map = new HashMap<>(4);
                map.put("code", outboundNo);
                map.put("errorMsg", StringsUtil.createI18Message(SysConstant.Outbound_Order_Does_Not_Exist));
                res.setFailed(res.getFailed() + dtoList.size());
                res.getFailedList().add(map);
                continue;
            }
            transactionTemplate.execute(status -> {
                for (UpdateTrackingNoImportWmsDto dto : dtoList) {
                    if (StrUtil.isNotBlank(dto.getShippingService())) {
                        if ("".equals(StaticDict.Lc_Carrier.getText(dto.getShippingService()))) {
                            HashMap<String, Object> map = new HashMap<>(4);
                            map.put("code", outboundNo);
                            map.put("errorMsg", "Shipping service/承运商[" + dto.getShippingService() + "] Not Found.Please check");
                            res.setFailed(res.getFailed() + 1);
                            res.getFailedList().add(map);
                            continue;
                        }
                    }
                    outboundPackageService.lambdaUpdate()
                            .eq(OrderOutboundPackageDo::getOutboundId, outboundDo.getId())
                            .eq(OrderOutboundPackageDo::getSerialNumber, dto.getSerialNumber())
                            .set(OrderOutboundPackageDo::getTrackingNumber, dto.getTrackingNumber())
                            .update();
//                if (StrUtil.isNotBlank(dto.getShippingService())) {
//                    outboundService.lambdaUpdate()
//                            .eq(OrderOutboundDo::getId, outboundDo.getId())
//                            .set(OrderOutboundDo::getCarrier, dto.getShippingService())
//                            .update();
//                }
                    res.setSuccess(res.getSuccess() + 1);
                }
                String trackingNumberStr = dtoList.stream().map(UpdateTrackingNoImportWmsDto::getTrackingNumber).filter(StrUtil::isNotBlank).collect(Collectors.joining(","));
                if (StrUtil.isNotBlank(trackingNumberStr)) {
                    List<UpdateTrackingNoImportWmsDto> shippingList = dtoList.stream().filter(i -> StrUtil.isNotBlank(i.getShippingService())).collect(Collectors.toList());
                    outboundService.lambdaUpdate()
                            .eq(OrderOutboundDo::getId, outboundDo.getId())
                            .set(OrderOutboundDo::getTrackingNumber, trackingNumberStr)
                            .set(CollUtil.isNotEmpty(shippingList), OrderOutboundDo::getCarrier, CollUtil.isNotEmpty(shippingList) ? shippingList.get(0).getShippingService() : null)
                            .update();
                }
                OperateRecordDo operateRecordDo = new OperateRecordDo();
                operateRecordDo.setContent(SysConstant.Outbound_Import_Tracking_Number);
                operateRecordDo.setContentValue("");
                operateRecordDo.setOperateTime(DateUtil.date());
                operateRecordDo.setOperateBy(userId);
                operateRecordDo.setOperateUsername(username);
                operateRecordDo.setType(OperateTypeEnum.OrderOutbound.getValue());
                operateRecordDo.setThirdId(outboundDo.getId());
                operateRecordDo.setDeleteFlag(0);
                operateRecordDos.add(operateRecordDo);
                successId.add(outboundDo.getId());
                return null;
            });
        }
        if ("jd_api".equals(from)) {
            ImportUpdateTrackingNumberAutoSendApiBo autoSendApiBo = ImportUpdateTrackingNumberAutoSendApiBo.builder().successId(successId).orderType(orderType).build();
            rabbitTemplate.convertAndSend(RabbitQueueConstant.OutBound_Labella_Order_AutoSend, JSON.toJSONString(autoSendApiBo));
        }
        if (CollUtil.isNotEmpty(operateRecordDos)) {
            rabbitTemplate.convertAndSend(RabbitQueueConstant.CrossMall_CMOrder_OperateList, JSON.toJSONString(operateRecordDos));
        }
        return res;
    }

    @Override
    public CommonBatchOperationVo importUpdatePackage(MultipartFile file, Long tenantId, Long userId, String username) {
        CommonBatchOperationVo res = new CommonBatchOperationVo();
        List<UpdatePackageImportWmsDto> importDtoList;
        try {
            importDtoList = ExcelUtils.importExcel(file, 1, 0, UpdatePackageImportWmsDto.class);
        } catch (Exception e) {
            throw new BizException(SysConstant.Excel_Template_Error);
        }
        int oldSize = importDtoList.size();
        res.setTotal(importDtoList.size());
        importDtoList = importDtoList.stream().filter(
                i -> StrUtil.isNotBlank(i.getOutboundNo()) && Objects.nonNull(i.getSerialNumber())
        ).collect(Collectors.toList());
        int newSize = importDtoList.size();
        if (oldSize != newSize) {
            HashMap<String, Object> map = new HashMap<>(4);
            map.put("code", "Exception");
            map.put("errorMsg", StringsUtil.createI18Message(SysConstant.Import_Required_Field_Blank));
            res.setFailed(res.getFailed() + oldSize - newSize);
            res.getFailedList().add(map);
        }
        if (importDtoList.isEmpty()) {
            return res;
        }
        List<String> materialCodeList = materialService.lambdaQuery()
                .eq(MaterialDo::getTenantId, tenantId)
                .list().stream().map(MaterialDo::getMaterialCode).collect(Collectors.toList());
        Map<String, OrderOutboundDo> outboundMatch = outboundService.list(Wrappers.<OrderOutboundDo>lambdaQuery()
                .eq(OrderOutboundDo::getTenantId, tenantId)
                .in(OrderOutboundDo::getOutboundNo, importDtoList.stream()
                        .map(UpdatePackageImportWmsDto::getOutboundNo).collect(Collectors.toSet()))
        ).stream().collect(Collectors.toMap(OrderOutboundDo::getOutboundNo, val -> val));
        Map<String, List<UpdatePackageImportWmsDto>> dataGroupNo = importDtoList.stream()
                .collect(Collectors.groupingBy(UpdatePackageImportWmsDto::getOutboundNo));
        for (Map.Entry<String, List<UpdatePackageImportWmsDto>> entry : dataGroupNo.entrySet()) {
            String outboundNo = entry.getKey();
            List<UpdatePackageImportWmsDto> dtoList = entry.getValue();
            OrderOutboundDo outboundDo = outboundMatch.get(outboundNo);
            if (Objects.isNull(outboundDo)) {
                HashMap<String, Object> map = new HashMap<>(4);
                map.put("code", outboundNo);
                map.put("errorMsg", "Outbound Order Num/出库单号[" + outboundNo + "]Wrong,Please check");
                res.setFailed(res.getFailed() + dtoList.size());
                res.getFailedList().add(map);
                continue;
            }
            Map<Integer, List<UpdatePackageImportWmsDto>> dtoListGroupBySeq = dtoList.stream().collect(Collectors.groupingBy(UpdatePackageImportWmsDto::getSerialNumber));
            for (Map.Entry<Integer, List<UpdatePackageImportWmsDto>> entryBySeq : dtoListGroupBySeq.entrySet()) {
                Integer serialNumber = entryBySeq.getKey();
                List<UpdatePackageImportWmsDto> dtoListBySeq = entryBySeq.getValue();
                UpdatePackageImportWmsDto dto = dtoListBySeq.get(0);
                String trackingNumber = dto.getTrackingNumber();
                String measurementUnit = dto.getMeasurementUnit();
                String weightUnit = dto.getWeightUnit();
                if (StrUtil.isNotBlank(measurementUnit) && !"cm".equalsIgnoreCase(measurementUnit) && !"in".equalsIgnoreCase(measurementUnit)) {
                    HashMap<String, Object> map = new HashMap<>(4);
                    map.put("code", outboundNo);
                    map.put("errorMsg", "Unit/尺寸单位 Wrong,Please check");
                    res.setFailed(res.getFailed() + dtoListBySeq.size());
                    res.getFailedList().add(map);
                    continue;
                }
                if (StrUtil.isNotBlank(weightUnit) && !"kg".equalsIgnoreCase(weightUnit) && !"lb".equalsIgnoreCase(weightUnit)) {
                    HashMap<String, Object> map = new HashMap<>(4);
                    map.put("code", outboundNo);
                    map.put("errorMsg", "Unit/重量单位 Wrong,Please check");
                    res.setFailed(res.getFailed() + dtoListBySeq.size());
                    res.getFailedList().add(map);
                    continue;
                }
                boolean materialCodeFlag = false;
                for (UpdatePackageImportWmsDto wmsDto : dtoListBySeq) {
                    if (StrUtil.isNotBlank(wmsDto.getMaterialCode()) && !materialCodeList.contains(wmsDto.getMaterialCode())) {
                        materialCodeFlag = true;
                        HashMap<String, Object> map = new HashMap<>(4);
                        map.put("code", outboundNo);
                        map.put("errorMsg", "Serial Number[" + serialNumber + "],Package Material Code/包材编码 Wrong,Please check");
                        res.setFailed(res.getFailed() + dtoListBySeq.size());
                        res.getFailedList().add(map);
                        break;
                    }
                }
                if (materialCodeFlag) {
                    continue;
                }
                OrderOutboundPackageDo packageDo = outboundPackageService.getOne(Wrappers.<OrderOutboundPackageDo>lambdaQuery()
                        .eq(OrderOutboundPackageDo::getOutboundId, outboundDo.getId())
                        .eq(OrderOutboundPackageDo::getSerialNumber, serialNumber)
                );
                if (Objects.isNull(packageDo)) {
                    HashMap<String, Object> map = new HashMap<>(4);
                    map.put("code", outboundNo);
                    map.put("errorMsg", "Serial Number/包裹序号 Wrong,Please check");
                    res.setFailed(res.getFailed() + dtoListBySeq.size());
                    res.getFailedList().add(map);
                    continue;
                }
                if (StrUtil.isNotBlank(trackingNumber)) {
                    packageDo.setTrackingNumber(trackingNumber);
                }
                if (StrUtil.isNotBlank(measurementUnit)) {
                    if ("cm".equalsIgnoreCase(measurementUnit)) {
                        if (Objects.nonNull(dto.getLength())) {
                            packageDo.setLength(dto.getLength().setScale(3, RoundingMode.HALF_UP));
                            packageDo.setLengthBs(dto.getLength().divide(BigDecimal.valueOf(2.54), 3, RoundingMode.HALF_UP));
                        }
                        if (Objects.nonNull(dto.getWidth())) {
                            packageDo.setWidth(dto.getWidth().setScale(3, RoundingMode.HALF_UP));
                            packageDo.setWidthBs(dto.getWidth().divide(BigDecimal.valueOf(2.54), 3, RoundingMode.HALF_UP));
                        }
                        if (Objects.nonNull(dto.getHeight())) {
                            packageDo.setHeight(dto.getHeight().setScale(3, RoundingMode.HALF_UP));
                            packageDo.setHeightBs(dto.getHeight().divide(BigDecimal.valueOf(2.54), 3, RoundingMode.HALF_UP));
                        }
                    }
                    if ("in".equalsIgnoreCase(measurementUnit)) {
                        if (Objects.nonNull(dto.getLength())) {
                            packageDo.setLength(dto.getLength().multiply(BigDecimal.valueOf(2.54)).setScale(3, RoundingMode.HALF_UP));
                            packageDo.setLengthBs(dto.getLength().setScale(3, RoundingMode.HALF_UP));
                        }
                        if (Objects.nonNull(dto.getWidth())) {
                            packageDo.setWidth(dto.getWidth().multiply(BigDecimal.valueOf(2.54)).setScale(3, RoundingMode.HALF_UP));
                            packageDo.setWidthBs(dto.getWidth().setScale(3, RoundingMode.HALF_UP));
                        }
                        if (Objects.nonNull(dto.getHeight())) {
                            packageDo.setHeight(dto.getHeight().multiply(BigDecimal.valueOf(2.54)).setScale(3, RoundingMode.HALF_UP));
                            packageDo.setHeightBs(dto.getHeight().setScale(3, RoundingMode.HALF_UP));
                        }
                    }
                }
                if (StrUtil.isNotBlank(weightUnit)) {
                    if ("kg".equalsIgnoreCase(weightUnit)) {
                        if (Objects.nonNull(dto.getWeight())) {
                            packageDo.setWeight(dto.getWeight().setScale(3, RoundingMode.HALF_UP));
                            packageDo.setWeightBs(dto.getWeight().multiply(BigDecimal.valueOf(2.2046)).setScale(3, RoundingMode.HALF_UP));
                        }
                    }
                    if ("lb".equalsIgnoreCase(weightUnit)) {
                        if (Objects.nonNull(dto.getWeight())) {
                            packageDo.setWeight(dto.getWeight().divide(BigDecimal.valueOf(2.2046), 3, RoundingMode.HALF_UP));
                            packageDo.setWeightBs(dto.getWeight().setScale(3, RoundingMode.HALF_UP));
                        }
                    }
                }
                outboundPackageService.updateById(packageDo);
                // 更新包材
                List<OrderOutboundPackageMaterialDo> packageMaterialDoList = JSONUtil.toList(JSONUtil.toJsonStr(dtoListBySeq), OrderOutboundPackageMaterialDo.class);
                packageMaterialDoList.forEach(materialDo -> materialDo.setPackageId(packageDo.getId()));
                packageMaterialDoList = packageMaterialDoList.stream().filter(materialDo -> Objects.nonNull(materialDo.getMaterialNum()) && StrUtil.isNotBlank(materialDo.getMaterialCode())).collect(Collectors.toList());
                if (CollUtil.isNotEmpty(packageMaterialDoList)) {
                    outboundPackageMaterialService.remove(Wrappers.<OrderOutboundPackageMaterialDo>lambdaQuery()
                            .eq(OrderOutboundPackageMaterialDo::getPackageId, packageDo.getId())
                    );
                    outboundPackageMaterialService.saveBatch(packageMaterialDoList);
                }
                res.setSuccess(res.getSuccess() + dtoListBySeq.size());
            }
            String trackingNumberStr = dtoList.stream().map(UpdatePackageImportWmsDto::getTrackingNumber).filter(StrUtil::isNotBlank).collect(Collectors.joining(","));
            if (StrUtil.isNotBlank(trackingNumberStr)) {
                outboundService.lambdaUpdate()
                        .eq(OrderOutboundDo::getId, outboundDo.getId())
                        .set(OrderOutboundDo::getTrackingNumber, trackingNumberStr)
                        .update();
            }
        }
        return res;
    }

    @Override
    public void printTransferPickingList(Long id, HttpServletResponse response) throws IOException {
        response.setHeader("Content-Type", "application/pdf");
        response.setHeader("Access-Control-Expose-Headers", "Content-Disposition");
        response.setHeader("Content-Disposition", "attachment;filename=printTransferPickingList.pdf");
        ServletOutputStream out = response.getOutputStream();
        buildTransferPickingListPdf(id, out);
    }

    @Override
    public void printTransferPickingListBatch(List<Long> ids, HttpServletResponse response) throws Exception {
        if (CollUtil.isEmpty(ids)) {
            throw new BizException(SysConstant.No_Data);
        }
        if (ids.size() > 100) {
            throw new BizException(SysConstant.Batch_Over_100);
        }
        response.setHeader("Content-Type", "application/pdf");
        response.setHeader("Access-Control-Expose-Headers", "Content-Disposition");
        response.setHeader("Content-Disposition", "attachment;filename=printTransferPickingListBatch.pdf");
        // 1. 并发生成每个pdf到内存
        List<CompletableFuture<byte[]>> futures = ids.stream()
                .map(id -> CompletableFuture.supplyAsync(() -> {
                    try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
                        buildTransferPickingListPdf(id, baos);
                        return baos.toByteArray();
                    } catch (Exception e) {
                        throw new RuntimeException("拣货单导出失败: " + id, e);
                    }
                }, threadPoolExecutor))
                .collect(Collectors.toList());

        List<byte[]> pdfBytesList = futures.stream()
                .map(CompletableFuture::join)
                .collect(Collectors.toList());

        // 2. 合并pdf
        Document document = new Document();
        PdfCopy copy = new PdfCopy(document, response.getOutputStream());
        document.open();
        for (byte[] pdfBytes : pdfBytesList) {
            PdfReader reader = new PdfReader(pdfBytes);
            for (int i = 1; i <= reader.getNumberOfPages(); i++) {
                copy.addPage(copy.getImportedPage(reader, i));
            }
            reader.close();
        }
        document.close();
        copy.close();
    }

    public void buildTransferPickingListPdf(Long id, OutputStream out) throws IOException {
        OrderOutboundDo outboundDo = outboundService.getById(id);
        String outboundNo = outboundDo.getOutboundNo();
        byte[] outboundNoBarcode = BarCodeUtils.generateBarCode128(
                outboundNo,
                true,
                false,
                256,
                15,
                0.3f,
                0f,
                0f
        );
        ByteArrayInputStream outboundNoBarcodeImg = new ByteArrayInputStream(outboundNoBarcode);
        //获取客户信息
        CompanyDo companyDo = companyService.getById(outboundDo.getCompanyId());
        String company = companyDo.getName() + "(" + companyDo.getCompanyCode() + ")";
        //通用参数
        Map<String, Object> data = new HashMap<>();
        data.put("outboundNo", outboundNo);
        data.put("company", company);
        data.put("printTime", DateUtil.date());
        data.put("outboundNoBarcode", Pictures.ofStream(outboundNoBarcodeImg, PictureType.PNG).size(225, 60).create());
        List<OrderOutboundProductDo> outboundProductDoList = outboundProductService.lambdaQuery()
                .eq(OrderOutboundProductDo::getOutboundId, id)
                .list();
        List<OutboundPickingDo> pickingDoList = outboundPickingService.lambdaQuery()
                .eq(OutboundPickingDo::getOrderType, OutboundOrderKind.outbound.getValue())
                .in(OutboundPickingDo::getOutboundProductId, outboundProductDoList.stream().map(OrderOutboundProductDo::getId).collect(Collectors.toList()))
                .list();
        Map<Long, StorageLocationDo> rackIdMatch = storageLocationService.lambdaQuery()
                .in(StorageLocationDo::getId, pickingDoList.stream().map(OutboundPickingDo::getRackId).collect(Collectors.toList()))
                .list().stream().collect(Collectors.toMap(StorageLocationDo::getId, val -> val));
        ConfigureBuilder configureBuilder = Configure.builder();
        XWPFTemplate template;
        ByteArrayOutputStream pdf;
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        //出库方式
        Integer outboundMode = outboundDo.getOutboundMode();
        switch (outboundMode) {
            case 1:
                //出库方式1:按sku出库
                Map<Long, ProductDo> productIdMatch = productService.lambdaQuery()
                        .in(ProductDo::getId, outboundProductDoList.stream().map(OrderOutboundProductDo::getProductId).collect(Collectors.toList()))
                        .list().stream().collect(Collectors.toMap(ProductDo::getId, val -> val));
                AtomicInteger i = new AtomicInteger(1);
                List<TransferPickListBySkuTableDto> dtoList1 = new ArrayList<>();
                for (OrderOutboundProductDo outboundProductDo : outboundProductDoList) {
                    ProductDo productDo = productIdMatch.get(outboundProductDo.getProductId());
                    if (productDo == null) {
                        continue;
                    }
                    TransferPickListBySkuTableDto dto = new TransferPickListBySkuTableDto();
                    dto.setNo(String.valueOf(i.getAndIncrement()));
                    dto.setSku(productDo.getSku());
                    dto.setJanCode(productDo.getJanCode());
                    dto.setProductName(productDo.getProductName());
                    List<OutboundPickingDo> pickingDoListBySku = pickingDoList.stream()
                            .filter(item -> item.getOutboundProductId().equals(outboundProductDo.getId()))
                            .collect(Collectors.toList());
                    //合并相同库位相同状态的数据
                    pickingDoListBySku.stream()
                            .collect(Collectors.toMap(k -> k.getRackId() + ":" + k.getPickFlag(), val -> val, (o1, o2) -> {
                                o1.setQty(o1.getQty() + o2.getQty());
                                return o1;
                            })).values().forEach(pickingDo -> {
                                HashMap<String, String> map = new HashMap<>(8);
                                map.put("cellCode", rackIdMatch.get(pickingDo.getRackId()).getCode());
                                map.put("qty", String.valueOf(pickingDo.getQty()));
                                map.put("state", pickingDo.getPickFlag() ? "Done" : "Not done");
                                dto.getSonList().add(map);
                            });
                    dtoList1.add(dto);
                }
                data.put(
                        "table",
                        this.getTransferPickListBySkuTableData(
                                dtoList1,
                                new DynamicTableDto(0, 0, 3, 7, 1)
                        )
                );
                data.put("skuKind", outboundProductDoList.size());
                data.put("skuTotalQty", outboundProductDoList.stream().map(OrderOutboundProductDo::getForecastQty).reduce(0, Integer::sum));
                //模板配置
                configureBuilder.bind("table", new DynamicTablePolicy());
                template = XWPFTemplate.compile(this.getClass().getClassLoader().getResourceAsStream("wordTemplates/transferPickListBySku.docx"), configureBuilder.build()).render(data);
                template.writeAndClose(bos);
                //转换为pdf
                pdf = AsposePdfUtils.word2pdf(new ByteArrayInputStream(bos.toByteArray()));
                out.write(pdf.toByteArray());
                bos.flush();
                out.flush();
                PoitlIOUtils.closeQuietlyMulti(template, bos, out, pdf);
                break;
            case 2:
                //出库方式2:按箱出库
                Set<String> boxTypeNoSet = outboundProductDoList.stream().map(OrderOutboundProductDo::getBoxTypeNumber).collect(Collectors.toSet());
                Map<String, InputBoxTypeNumberVo> boxMatch = new HashMap<>();
                if (CollUtil.isNotEmpty(boxTypeNoSet)) {
                    InputBoxTypeQueryBo boxTypeQueryBo = new InputBoxTypeQueryBo();
                    boxTypeQueryBo.setCurrent(1);
                    boxTypeQueryBo.setPageSize(boxTypeNoSet.size());
                    boxTypeQueryBo.setBoxTypeNumberList(boxTypeNoSet);
                    boxMatch = boxService.getBoxTypeNumberListPage(boxTypeQueryBo).getData().stream().collect(Collectors.toMap(InputBoxTypeNumberVo::getBoxTypeNumber, val -> val, (o1, o2) -> o1));
                }
                AtomicInteger j = new AtomicInteger(1);
                List<TransferPickListByBoxTableDto> dtoList2 = new ArrayList<>();
                for (OrderOutboundProductDo outboundProductDo : outboundProductDoList) {
                    TransferPickListByBoxTableDto dto = new TransferPickListByBoxTableDto();
                    dto.setNo(String.valueOf(j.getAndIncrement()));
                    dto.setBoxTypeNumber(outboundProductDo.getBoxTypeNumber());
                    InputBoxTypeNumberVo boxTypeNumberVo = boxMatch.get(outboundProductDo.getBoxTypeNumber());
                    if (boxTypeNumberVo != null) {
                        String itfCode = boxTypeNumberVo.getCustomizeBarcode();
                        String skuInfo = boxTypeNumberVo.getProductList().stream().map(item -> item.getSku() + "*" + item.getPerBoxQty()).collect(Collectors.joining(","));
                        dto.setItfCodeAndSkuInfo((StrUtil.isBlank(itfCode) ? "-" : itfCode) + "/" + (StrUtil.isBlank(skuInfo) ? "-" : skuInfo));
                    } else {
                        dto.setItfCodeAndSkuInfo("");
                    }
                    List<OutboundPickingDo> pickingDoListByBox = pickingDoList.stream()
                            .filter(item -> item.getOutboundProductId().equals(outboundProductDo.getId()))
                            .collect(Collectors.toList());
                    //合并相同库位相同状态的数据
                    pickingDoListByBox.stream()
                            .collect(Collectors.toMap(k -> k.getRackId() + ":" + k.getPickFlag(), val -> val, (o1, o2) -> {
                                o1.setQty(o1.getQty() + o2.getQty());
                                return o1;
                            })).values().forEach(pickingDo -> {
                                HashMap<String, String> map = new HashMap<>(8);
                                map.put("cellCode", rackIdMatch.get(pickingDo.getRackId()).getCode());
                                map.put("qty", String.valueOf(pickingDo.getQty()));
                                map.put("state", pickingDo.getPickFlag() ? "Done" : "Not done");
                                dto.getSonList().add(map);
                            });
                    dtoList2.add(dto);
                }
                data.put(
                        "table",
                        this.getTransferPickListByBoxTableData(
                                dtoList2,
                                new DynamicTableDto(0, 0, 2, 6, 1)
                        )
                );
                data.put("boxKind", outboundProductDoList.size());
                data.put("boxTotalQty", outboundProductDoList.stream().map(OrderOutboundProductDo::getForecastBoxQty).reduce(0, Integer::sum));
                //模板配置
                configureBuilder.bind("table", new DynamicTablePolicy());
                template = XWPFTemplate.compile(this.getClass().getClassLoader().getResourceAsStream("wordTemplates/transferPickListByBox.docx"), configureBuilder.build()).render(data);
                template.writeAndClose(bos);
                //转换为pdf
                pdf = AsposePdfUtils.word2pdf(new ByteArrayInputStream(bos.toByteArray()));
                out.write(pdf.toByteArray());
                bos.flush();
                out.flush();
                PoitlIOUtils.closeQuietlyMulti(template, bos, out, pdf);
                break;
            default:
        }
    }

    @Override
    public void printExpressSheet(PrintExpressSheetBo bo, HttpServletResponse response) throws Exception {
        OrderOutboundDo outboundDo = outboundService.getById(bo.getId());
        List<OrderOutboundPackageDo> packageDoList = outboundPackageService.lambdaQuery()
                .eq(OrderOutboundPackageDo::getOutboundId, outboundDo.getId())
                .list();
        List<Long> labelFileIdList = packageDoList.stream().map(OrderOutboundPackageDo::getLabelFileId).filter(Objects::nonNull).collect(Collectors.toList());
        if (CollUtil.isEmpty(labelFileIdList)) {
            throw new BizException(SysConstant.No_Express_Sheet);
        }
        if (bo.isRepeatPrinting()) {
            if (Objects.nonNull(outboundDo.getFaceSheetPrintTime())) {
                //重复打印提示
                response.setHeader("Repeat-Printing", "true");
            } else {
                response.setHeader("Repeat-Printing", "false");
            }
        }
        //修改面单打印时间
        outboundDo.setFaceSheetPrintTime(DateUtil.date());
        outboundService.updateById(outboundDo);
        response.setHeader("Content-Type", "application/pdf");
        response.setHeader("Access-Control-Expose-Headers", "Content-Disposition,Repeat-Printing");
        response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(outboundDo.getOutboundNo() + "-ExpressFaceSheet-" + DateUtil.date() + ".pdf", "UTF-8"));
        List<File> files = new ArrayList<>();
        for (Long labelFileId : labelFileIdList) {
            File file = fileService.getFileByFileId(labelFileId);
            if (Objects.nonNull(file)) {
                files.add(file);
            }
        }
        if (CollUtil.isNotEmpty(files)) {
            this.mergeFilesToPdf(files, response.getOutputStream());
        }
    }

    /**
     * 将多个文件合并为一个PDF，每个文件生成一页。
     * 支持的文件格式：PDF、jpg/jpeg/png/bmp/gif 图片。
     *
     * @param files 要合并的文件列表 注意：只能是pdf或图片格式
     * @param os    合并后生成的PDF输出流
     * @throws Exception 可能抛出异常
     */
    private void mergeFilesToPdf(List<File> files, OutputStream os) throws Exception {
        // 创建目标PDF文档和 PdfCopy 对象
        Document document = new Document();
        PdfCopy copy = new PdfCopy(document, os);
        document.open();

        // 遍历所有文件
        for (File file : files) {
            String fileName = file.getName().toLowerCase();
            if (fileName.endsWith(".pdf")) {
                // 如果是 PDF 文件，读取并添加第一页
                PdfReader reader = new PdfReader(file.getAbsolutePath());
                if (reader.getNumberOfPages() >= 1) {
                    PdfImportedPage page = copy.getImportedPage(reader, 1);
                    copy.addPage(page);
                }
                reader.close();
            } else if (fileName.endsWith(".jpg") || fileName.endsWith(".jpeg") ||
                       fileName.endsWith(".png") || fileName.endsWith(".bmp") ||
                       fileName.endsWith(".gif")) {
                // 如果是图片文件，先将图片转换为一个临时的PDF页面
                ByteArrayOutputStream baos = new ByteArrayOutputStream();
                // 这里使用 A4 纸作为临时PDF页面大小，需要根据面单尺寸进行调整
                Document tempDoc = new Document(PageSize.A4);
                PdfWriter.getInstance(tempDoc, baos);
                tempDoc.open();
                // 读取图片
                Image image = Image.getInstance(file.getAbsolutePath());
                // 按页面大小适当缩放（此处预留50个单位边距，可根据需要修改）
                image.scaleToFit(tempDoc.getPageSize().getWidth() - 50, tempDoc.getPageSize().getHeight() - 50);
                // 居中显示图片
                image.setAbsolutePosition(
                        (tempDoc.getPageSize().getWidth() - image.getScaledWidth()) / 2,
                        (tempDoc.getPageSize().getHeight() - image.getScaledHeight()) / 2);
                tempDoc.add(image);
                tempDoc.close();

                // 将临时PDF读取进来，并添加第一页
                PdfReader reader = new PdfReader(baos.toByteArray());
                PdfImportedPage page = copy.getImportedPage(reader, 1);
                copy.addPage(page);
                reader.close();
            } else {
                // 如果格式不在支持列表内，可选择忽略或作其他处理
//                System.out.println("不支持的文件格式：" + file.getAbsolutePath());
                throw new BizException(SysConstant.Unsupported_Courier_File_Format, file.getAbsolutePath());
            }
        }
        document.close();
        os.flush();
        os.close();
    }

    private DynamicTableDto getBoxShippingListTableData(List<BoxShippingListTableDto> dtoList, DynamicTableDto dynamicTableDto) {
        for (BoxShippingListTableDto dto : dtoList) {
            RowRenderData rowRenderData = Rows.of(
                    Cells.of(dto.getNo()).create(),
                    Cells.of(dto.getBoxTypeNumber()).create(),
                    Cells.of(dto.getCustomizeBarcode()).create(),
                    Cells.of(dto.getQty()).create()
            ).center().create();
            dynamicTableDto.getDataList().add(rowRenderData);
        }
        return dynamicTableDto;
    }

    private DynamicTableDto getTransferPickListByBoxTableData(List<TransferPickListByBoxTableDto> dtoList, DynamicTableDto tableDto) {
        for (TransferPickListByBoxTableDto dto : dtoList) {
            tableDto.getGroupMap().put(dto.getNo(), dto.getSonList().size());
            ByteArrayInputStream barcodeImg = null;
            if (StrUtil.isNotBlank(dto.getBoxTypeNumber())) {
                //入库单号条形
                byte[] barcode = BarCodeUtils.generateBarCode128(
                        dto.getBoxTypeNumber(),
                        false,
                        false,
                        256,
                        15,
                        0.3f,
                        0,
                        0
                );
                if (barcode != null) {
                    barcodeImg = new ByteArrayInputStream(barcode);
                }
            }
            for (Map<String, String> sonMap : dto.getSonList()) {
                RowRenderData rowRenderData = Rows.of(
                        Cells.of(dto.getNo()).create(),
                        Objects.isNull(barcodeImg)
                                ? Cells.of("-").create()
                                : Cells.of(Pictures.ofStream(barcodeImg, PictureType.PNG).size(220, 60).create()).create(),
                        Cells.of(dto.getItfCodeAndSkuInfo()).create(),
                        Cells.of(sonMap.get("cellCode")).create(),
                        Cells.of(sonMap.get("qty")).create(),
                        Cells.of(sonMap.get("state")).create()
                ).center().create();
                tableDto.getDataList().add(rowRenderData);
            }
        }
        return tableDto;
    }

    private DynamicTableDto getTransferPickListBySkuTableData(List<TransferPickListBySkuTableDto> dtoList, DynamicTableDto tableDto) {
        for (TransferPickListBySkuTableDto dto : dtoList) {
            tableDto.getGroupMap().put(dto.getNo(), dto.getSonList().size());
            ByteArrayInputStream barcodeImg = null;
            if (StrUtil.isNotBlank(dto.getJanCode())) {
                //产品条码
                byte[] barcode = BarCodeUtils.generateBarCode128(
                        dto.getJanCode(),
                        false,
                        false,
                        256,
                        15,
                        0.3f,
                        0,
                        0
                );
                if (barcode != null) {
                    barcodeImg = new ByteArrayInputStream(barcode);
                }
            }
            for (Map<String, String> sonMap : dto.getSonList()) {
                RowRenderData rowRenderData = Rows.of(
                        Cells.of(dto.getNo()).create(),
                        Cells.of(dto.getSku()).create(),
                        Objects.isNull(barcodeImg)
                                ? Cells.of("-").create()
                                : Cells.of(Pictures.ofStream(barcodeImg, PictureType.PNG).size(170, 50).create()).create(),
                        Cells.of(dto.getProductName()).create(),
                        Cells.of(sonMap.get("cellCode")).create(),
                        Cells.of(sonMap.get("qty")).create(),
                        Cells.of(sonMap.get("state")).create()
                ).center().create();
                tableDto.getDataList().add(rowRenderData);
            }
        }
        return tableDto;
    }

    public DynamicTableDto getSkuShippingListTableData(List<SkuShippingListTableDto> dtoList, DynamicTableDto tableDto) {
        for (SkuShippingListTableDto dto : dtoList) {
            ByteArrayInputStream barcodeImg = null;
            if (StrUtil.isNotBlank(dto.getJancode())) {
                byte[] barcode = BarCodeUtils.generateBarCode128(
                        dto.getJancode(),
                        true,
                        false,
                        256,
                        15,
                        0.4f,
                        0,
                        0
                );
                if (barcode != null) {
                    barcodeImg = new ByteArrayInputStream(barcode);
                }
            }
            RowRenderData rowRenderData = Rows.of(
                    Cells.of(dto.getNo()).create(),
                    Cells.of(dto.getSku()).create(),
                    Objects.isNull(barcodeImg) ? Cells.of("-").create() : Cells.of(Pictures.ofStream(barcodeImg, PictureType.PNG).size(160, 40).create()).create(),
                    Cells.of(dto.getProductName()).create(),
                    Cells.of(dto.getQty()).create()
            ).center().create();
            tableDto.getDataList().add(rowRenderData);
        }
        return tableDto;
    }
}
